Skip to content

Commit e31b3e9

Browse files
authored
Merge pull request microsoft#21627 from Microsoft/fixExtendsNever
Fix 'T extends never' in conditional types
2 parents d629607 + d5e2f49 commit e31b3e9

File tree

7 files changed

+109
-49
lines changed

7 files changed

+109
-49
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ namespace ts {
315315

316316
const anyType = createIntrinsicType(TypeFlags.Any, "any");
317317
const autoType = createIntrinsicType(TypeFlags.Any, "any");
318+
const wildcardType = createIntrinsicType(TypeFlags.Any, "any");
318319
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
319320
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
320321
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
@@ -8150,9 +8151,10 @@ namespace ts {
81508151
// Instantiate extends type without instantiating any 'infer T' type parameters
81518152
const extendsType = instantiateType(baseExtendsType, mapper);
81528153
// Return falseType for a definitely false extends check. We check an instantations of the two
8153-
// types with type parameters mapped to any, the most permissive instantiations possible. If those
8154-
// are not related, then no instatiations will be and we can just return the false branch type.
8155-
if (!isTypeAssignableTo(getAnyInstantiation(checkType), getAnyInstantiation(extendsType))) {
8154+
// types with type parameters mapped to the wildcard type, the most permissive instantiations
8155+
// possible (the wildcard type is assignable to and from all types). If those are not related,
8156+
// then no instatiations will be and we can just return the false branch type.
8157+
if (!isTypeAssignableTo(getWildcardInstantiation(checkType), getWildcardInstantiation(extendsType))) {
81568158
return instantiateType(baseFalseType, mapper);
81578159
}
81588160
// The check could be true for some instantiation
@@ -8615,8 +8617,8 @@ namespace ts {
86158617
return t => t === source ? target : baseMapper(t);
86168618
}
86178619

8618-
function anyMapper(type: Type) {
8619-
return type.flags & TypeFlags.TypeParameter ? anyType : type;
8620+
function wildcardMapper(type: Type) {
8621+
return type.flags & TypeFlags.TypeParameter ? wildcardType : type;
86208622
}
86218623

86228624
function cloneTypeParameter(typeParameter: TypeParameter): TypeParameter {
@@ -8873,9 +8875,9 @@ namespace ts {
88738875
return type;
88748876
}
88758877

8876-
function getAnyInstantiation(type: Type) {
8878+
function getWildcardInstantiation(type: Type) {
88778879
return type.flags & (TypeFlags.Primitive | TypeFlags.Any | TypeFlags.Never) ? type :
8878-
type.resolvedAnyInstantiation || (type.resolvedAnyInstantiation = instantiateType(type, anyMapper));
8880+
type.wildcardInstantiation || (type.wildcardInstantiation = instantiateType(type, wildcardMapper));
88798881
}
88808882

88818883
function instantiateIndexInfo(info: IndexInfo, mapper: TypeMapper): IndexInfo {
@@ -9282,7 +9284,7 @@ namespace ts {
92829284
function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map<RelationComparisonResult>, errorReporter?: ErrorReporter) {
92839285
const s = source.flags;
92849286
const t = target.flags;
9285-
if (t & TypeFlags.Any || s & TypeFlags.Never) return true;
9287+
if (t & TypeFlags.Any || s & TypeFlags.Never || source === wildcardType) return true;
92869288
if (t & TypeFlags.Never) return false;
92879289
if (s & TypeFlags.StringLike && t & TypeFlags.String) return true;
92889290
if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral &&

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3565,7 +3565,7 @@ namespace ts {
35653565
aliasSymbol?: Symbol; // Alias associated with type
35663566
aliasTypeArguments?: Type[]; // Alias type arguments (if any)
35673567
/* @internal */
3568-
resolvedAnyInstantiation?: Type; // Instantiation with type parameters mapped to any
3568+
wildcardInstantiation?: Type; // Instantiation with type parameters mapped to wildcard type
35693569
}
35703570

35713571
/* @internal */

tests/baselines/reference/conditionalTypes1.errors.txt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,12 +304,12 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(157,5): error TS2
304304
type And<A extends boolean, B extends boolean> = If<A, B, false>;
305305
type Or<A extends boolean, B extends boolean> = If<A, true, B>;
306306

307-
type isString<T> = Extends<T, string>;
307+
type IsString<T> = Extends<T, string>;
308308

309-
type Q1 = isString<number>; // false
310-
type Q2 = isString<"abc">; // true
311-
type Q3 = isString<any>; // boolean
312-
type Q4 = isString<never>; // boolean
309+
type Q1 = IsString<number>; // false
310+
type Q2 = IsString<"abc">; // true
311+
type Q3 = IsString<any>; // boolean
312+
type Q4 = IsString<never>; // boolean
313313

314314
type N1 = Not<false>; // true
315315
type N2 = Not<true>; // false
@@ -338,4 +338,10 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(157,5): error TS2
338338
type T40 = never extends never ? true : false; // true
339339
type T41 = number extends never ? true : false; // false
340340
type T42 = never extends number ? true : false; // boolean
341+
342+
type IsNever<T> = T extends never ? true : false;
343+
344+
type T50 = IsNever<never>; // true
345+
type T51 = IsNever<number>; // false
346+
type T52 = IsNever<any>; // false
341347

tests/baselines/reference/conditionalTypes1.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,12 @@ type Not<C extends boolean> = If<C, false, true>;
164164
type And<A extends boolean, B extends boolean> = If<A, B, false>;
165165
type Or<A extends boolean, B extends boolean> = If<A, true, B>;
166166

167-
type isString<T> = Extends<T, string>;
167+
type IsString<T> = Extends<T, string>;
168168

169-
type Q1 = isString<number>; // false
170-
type Q2 = isString<"abc">; // true
171-
type Q3 = isString<any>; // boolean
172-
type Q4 = isString<never>; // boolean
169+
type Q1 = IsString<number>; // false
170+
type Q2 = IsString<"abc">; // true
171+
type Q3 = IsString<any>; // boolean
172+
type Q4 = IsString<never>; // boolean
173173

174174
type N1 = Not<false>; // true
175175
type N2 = Not<true>; // false
@@ -198,6 +198,12 @@ type O9 = Or<boolean, boolean>; // boolean
198198
type T40 = never extends never ? true : false; // true
199199
type T41 = number extends never ? true : false; // false
200200
type T42 = never extends number ? true : false; // boolean
201+
202+
type IsNever<T> = T extends never ? true : false;
203+
204+
type T50 = IsNever<never>; // true
205+
type T51 = IsNever<number>; // false
206+
type T52 = IsNever<any>; // false
201207

202208

203209
//// [conditionalTypes1.js]
@@ -376,11 +382,11 @@ declare type If<C extends boolean, T, F> = C extends true ? T : F;
376382
declare type Not<C extends boolean> = If<C, false, true>;
377383
declare type And<A extends boolean, B extends boolean> = If<A, B, false>;
378384
declare type Or<A extends boolean, B extends boolean> = If<A, true, B>;
379-
declare type isString<T> = Extends<T, string>;
380-
declare type Q1 = isString<number>;
381-
declare type Q2 = isString<"abc">;
382-
declare type Q3 = isString<any>;
383-
declare type Q4 = isString<never>;
385+
declare type IsString<T> = Extends<T, string>;
386+
declare type Q1 = IsString<number>;
387+
declare type Q2 = IsString<"abc">;
388+
declare type Q3 = IsString<any>;
389+
declare type Q4 = IsString<never>;
384390
declare type N1 = Not<false>;
385391
declare type N2 = Not<true>;
386392
declare type N3 = Not<boolean>;
@@ -405,3 +411,7 @@ declare type O9 = Or<boolean, boolean>;
405411
declare type T40 = never extends never ? true : false;
406412
declare type T41 = number extends never ? true : false;
407413
declare type T42 = never extends number ? true : false;
414+
declare type IsNever<T> = T extends never ? true : false;
415+
declare type T50 = IsNever<never>;
416+
declare type T51 = IsNever<number>;
417+
declare type T52 = IsNever<any>;

tests/baselines/reference/conditionalTypes1.symbols

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -645,27 +645,27 @@ type Or<A extends boolean, B extends boolean> = If<A, true, B>;
645645
>A : Symbol(A, Decl(conditionalTypes1.ts, 163, 8))
646646
>B : Symbol(B, Decl(conditionalTypes1.ts, 163, 26))
647647

648-
type isString<T> = Extends<T, string>;
649-
>isString : Symbol(isString, Decl(conditionalTypes1.ts, 163, 63))
648+
type IsString<T> = Extends<T, string>;
649+
>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 163, 63))
650650
>T : Symbol(T, Decl(conditionalTypes1.ts, 165, 14))
651651
>Extends : Symbol(Extends, Decl(conditionalTypes1.ts, 157, 1))
652652
>T : Symbol(T, Decl(conditionalTypes1.ts, 165, 14))
653653

654-
type Q1 = isString<number>; // false
654+
type Q1 = IsString<number>; // false
655655
>Q1 : Symbol(Q1, Decl(conditionalTypes1.ts, 165, 38))
656-
>isString : Symbol(isString, Decl(conditionalTypes1.ts, 163, 63))
656+
>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 163, 63))
657657

658-
type Q2 = isString<"abc">; // true
658+
type Q2 = IsString<"abc">; // true
659659
>Q2 : Symbol(Q2, Decl(conditionalTypes1.ts, 167, 27))
660-
>isString : Symbol(isString, Decl(conditionalTypes1.ts, 163, 63))
660+
>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 163, 63))
661661

662-
type Q3 = isString<any>; // boolean
662+
type Q3 = IsString<any>; // boolean
663663
>Q3 : Symbol(Q3, Decl(conditionalTypes1.ts, 168, 26))
664-
>isString : Symbol(isString, Decl(conditionalTypes1.ts, 163, 63))
664+
>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 163, 63))
665665

666-
type Q4 = isString<never>; // boolean
666+
type Q4 = IsString<never>; // boolean
667667
>Q4 : Symbol(Q4, Decl(conditionalTypes1.ts, 169, 24))
668-
>isString : Symbol(isString, Decl(conditionalTypes1.ts, 163, 63))
668+
>IsString : Symbol(IsString, Decl(conditionalTypes1.ts, 163, 63))
669669

670670
type N1 = Not<false>; // true
671671
>N1 : Symbol(N1, Decl(conditionalTypes1.ts, 170, 26))
@@ -760,3 +760,20 @@ type T41 = number extends never ? true : false; // false
760760
type T42 = never extends number ? true : false; // boolean
761761
>T42 : Symbol(T42, Decl(conditionalTypes1.ts, 197, 47))
762762

763+
type IsNever<T> = T extends never ? true : false;
764+
>IsNever : Symbol(IsNever, Decl(conditionalTypes1.ts, 198, 47))
765+
>T : Symbol(T, Decl(conditionalTypes1.ts, 200, 13))
766+
>T : Symbol(T, Decl(conditionalTypes1.ts, 200, 13))
767+
768+
type T50 = IsNever<never>; // true
769+
>T50 : Symbol(T50, Decl(conditionalTypes1.ts, 200, 49))
770+
>IsNever : Symbol(IsNever, Decl(conditionalTypes1.ts, 198, 47))
771+
772+
type T51 = IsNever<number>; // false
773+
>T51 : Symbol(T51, Decl(conditionalTypes1.ts, 202, 26))
774+
>IsNever : Symbol(IsNever, Decl(conditionalTypes1.ts, 198, 47))
775+
776+
type T52 = IsNever<any>; // false
777+
>T52 : Symbol(T52, Decl(conditionalTypes1.ts, 203, 27))
778+
>IsNever : Symbol(IsNever, Decl(conditionalTypes1.ts, 198, 47))
779+

tests/baselines/reference/conditionalTypes1.types

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -717,27 +717,27 @@ type Or<A extends boolean, B extends boolean> = If<A, true, B>;
717717
>true : true
718718
>B : B
719719

720-
type isString<T> = Extends<T, string>;
721-
>isString : Extends<T, string>
720+
type IsString<T> = Extends<T, string>;
721+
>IsString : Extends<T, string>
722722
>T : T
723723
>Extends : Extends<T, U>
724724
>T : T
725725

726-
type Q1 = isString<number>; // false
726+
type Q1 = IsString<number>; // false
727727
>Q1 : false
728-
>isString : Extends<T, string>
728+
>IsString : Extends<T, string>
729729

730-
type Q2 = isString<"abc">; // true
730+
type Q2 = IsString<"abc">; // true
731731
>Q2 : true
732-
>isString : Extends<T, string>
732+
>IsString : Extends<T, string>
733733

734-
type Q3 = isString<any>; // boolean
734+
type Q3 = IsString<any>; // boolean
735735
>Q3 : boolean
736-
>isString : Extends<T, string>
736+
>IsString : Extends<T, string>
737737

738-
type Q4 = isString<never>; // boolean
738+
type Q4 = IsString<never>; // boolean
739739
>Q4 : boolean
740-
>isString : Extends<T, string>
740+
>IsString : Extends<T, string>
741741

742742
type N1 = Not<false>; // true
743743
>N1 : true
@@ -864,3 +864,22 @@ type T42 = never extends number ? true : false; // boolean
864864
>true : true
865865
>false : false
866866

867+
type IsNever<T> = T extends never ? true : false;
868+
>IsNever : IsNever<T>
869+
>T : T
870+
>T : T
871+
>true : true
872+
>false : false
873+
874+
type T50 = IsNever<never>; // true
875+
>T50 : true
876+
>IsNever : IsNever<T>
877+
878+
type T51 = IsNever<number>; // false
879+
>T51 : false
880+
>IsNever : IsNever<T>
881+
882+
type T52 = IsNever<any>; // false
883+
>T52 : false
884+
>IsNever : IsNever<T>
885+

tests/cases/conformance/types/conditional/conditionalTypes1.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,12 @@ type Not<C extends boolean> = If<C, false, true>;
166166
type And<A extends boolean, B extends boolean> = If<A, B, false>;
167167
type Or<A extends boolean, B extends boolean> = If<A, true, B>;
168168

169-
type isString<T> = Extends<T, string>;
169+
type IsString<T> = Extends<T, string>;
170170

171-
type Q1 = isString<number>; // false
172-
type Q2 = isString<"abc">; // true
173-
type Q3 = isString<any>; // boolean
174-
type Q4 = isString<never>; // boolean
171+
type Q1 = IsString<number>; // false
172+
type Q2 = IsString<"abc">; // true
173+
type Q3 = IsString<any>; // boolean
174+
type Q4 = IsString<never>; // boolean
175175

176176
type N1 = Not<false>; // true
177177
type N2 = Not<true>; // false
@@ -200,3 +200,9 @@ type O9 = Or<boolean, boolean>; // boolean
200200
type T40 = never extends never ? true : false; // true
201201
type T41 = number extends never ? true : false; // false
202202
type T42 = never extends number ? true : false; // boolean
203+
204+
type IsNever<T> = T extends never ? true : false;
205+
206+
type T50 = IsNever<never>; // true
207+
type T51 = IsNever<number>; // false
208+
type T52 = IsNever<any>; // false

0 commit comments

Comments
 (0)