Skip to content

Commit aa2709f

Browse files
committed
Handle propagating substitution types to retain constraints on positions through instantiation
1 parent 88623ad commit aa2709f

10 files changed

+195
-37
lines changed

src/compiler/checker.ts

+57-33
Large diffs are not rendered by default.

src/compiler/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4064,7 +4064,7 @@ namespace ts {
40644064
// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution
40654065
// types disappear upon instantiation (just like type parameters).
40664066
export interface SubstitutionType extends InstantiableType {
4067-
typeVariable: TypeVariable; // Target type variable
4067+
typeVariable: Type; // Target type variable
40684068
substitute: Type; // Type to substitute for type parameter
40694069
negatedTypes?: Type[]; // Types proven that this type variables _doesn't_ extend
40704070
}
@@ -4153,6 +4153,7 @@ namespace ts {
41534153
InferUnionTypes = 1 << 0, // Infer union types for disjoint candidates (otherwise unknownType)
41544154
NoDefault = 1 << 1, // Infer unknownType for no inferences (otherwise anyType or emptyObjectType)
41554155
AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType)
4156+
SkipConstraintCheck = 1 << 3, // Skip checking weather the inference is assignable to the constraint (otherwise is replaced with constraint)
41564157
}
41574158

41584159
/**

tests/baselines/reference/api/tsserverlibrary.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2300,7 +2300,7 @@ declare namespace ts {
23002300
resolvedFalseType?: Type;
23012301
}
23022302
interface SubstitutionType extends InstantiableType {
2303-
typeVariable: TypeVariable;
2303+
typeVariable: Type;
23042304
substitute: Type;
23052305
negatedTypes?: Type[];
23062306
}

tests/baselines/reference/api/typescript.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2300,7 +2300,7 @@ declare namespace ts {
23002300
resolvedFalseType?: Type;
23012301
}
23022302
interface SubstitutionType extends InstantiableType {
2303-
typeVariable: TypeVariable;
2303+
typeVariable: Type;
23042304
substitute: Type;
23052305
negatedTypes?: Type[];
23062306
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
tests/cases/compiler/conditionalTypeGenericAssignability.ts(3,5): error TS2322: Type '0' is not assignable to type 'Extract<keyof T, string>'.
2+
tests/cases/compiler/conditionalTypeGenericAssignability.ts(7,5): error TS2322: Type '"foo"' is not assignable to type 'Exclude<keyof T, string>'.
3+
4+
5+
==== tests/cases/compiler/conditionalTypeGenericAssignability.ts (2 errors) ====
6+
function f1<T extends { foo: unknown; 0: unknown }>(_a: T, b: Extract<keyof T, string>) {
7+
b = "foo"; // succeeds
8+
b = 0; // errors
9+
~
10+
!!! error TS2322: Type '0' is not assignable to type 'Extract<keyof T, string>'.
11+
}
12+
13+
function f2<T extends { foo: unknown; 0: unknown }>(_a: T, b: Exclude<keyof T, string>) {
14+
b = "foo"; // errors
15+
~
16+
!!! error TS2322: Type '"foo"' is not assignable to type 'Exclude<keyof T, string>'.
17+
b = 0; // succeeds
18+
}
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//// [conditionalTypeGenericAssignability.ts]
2+
function f1<T extends { foo: unknown; 0: unknown }>(_a: T, b: Extract<keyof T, string>) {
3+
b = "foo"; // succeeds
4+
b = 0; // errors
5+
}
6+
7+
function f2<T extends { foo: unknown; 0: unknown }>(_a: T, b: Exclude<keyof T, string>) {
8+
b = "foo"; // errors
9+
b = 0; // succeeds
10+
}
11+
12+
13+
//// [conditionalTypeGenericAssignability.js]
14+
"use strict";
15+
function f1(_a, b) {
16+
b = "foo"; // succeeds
17+
b = 0; // errors
18+
}
19+
function f2(_a, b) {
20+
b = "foo"; // errors
21+
b = 0; // succeeds
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/compiler/conditionalTypeGenericAssignability.ts ===
2+
function f1<T extends { foo: unknown; 0: unknown }>(_a: T, b: Extract<keyof T, string>) {
3+
>f1 : Symbol(f1, Decl(conditionalTypeGenericAssignability.ts, 0, 0))
4+
>T : Symbol(T, Decl(conditionalTypeGenericAssignability.ts, 0, 12))
5+
>foo : Symbol(foo, Decl(conditionalTypeGenericAssignability.ts, 0, 23))
6+
>0 : Symbol(0, Decl(conditionalTypeGenericAssignability.ts, 0, 37))
7+
>_a : Symbol(_a, Decl(conditionalTypeGenericAssignability.ts, 0, 52))
8+
>T : Symbol(T, Decl(conditionalTypeGenericAssignability.ts, 0, 12))
9+
>b : Symbol(b, Decl(conditionalTypeGenericAssignability.ts, 0, 58))
10+
>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --))
11+
>T : Symbol(T, Decl(conditionalTypeGenericAssignability.ts, 0, 12))
12+
13+
b = "foo"; // succeeds
14+
>b : Symbol(b, Decl(conditionalTypeGenericAssignability.ts, 0, 58))
15+
16+
b = 0; // errors
17+
>b : Symbol(b, Decl(conditionalTypeGenericAssignability.ts, 0, 58))
18+
}
19+
20+
function f2<T extends { foo: unknown; 0: unknown }>(_a: T, b: Exclude<keyof T, string>) {
21+
>f2 : Symbol(f2, Decl(conditionalTypeGenericAssignability.ts, 3, 1))
22+
>T : Symbol(T, Decl(conditionalTypeGenericAssignability.ts, 5, 12))
23+
>foo : Symbol(foo, Decl(conditionalTypeGenericAssignability.ts, 5, 23))
24+
>0 : Symbol(0, Decl(conditionalTypeGenericAssignability.ts, 5, 37))
25+
>_a : Symbol(_a, Decl(conditionalTypeGenericAssignability.ts, 5, 52))
26+
>T : Symbol(T, Decl(conditionalTypeGenericAssignability.ts, 5, 12))
27+
>b : Symbol(b, Decl(conditionalTypeGenericAssignability.ts, 5, 58))
28+
>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --))
29+
>T : Symbol(T, Decl(conditionalTypeGenericAssignability.ts, 5, 12))
30+
31+
b = "foo"; // errors
32+
>b : Symbol(b, Decl(conditionalTypeGenericAssignability.ts, 5, 58))
33+
34+
b = 0; // succeeds
35+
>b : Symbol(b, Decl(conditionalTypeGenericAssignability.ts, 5, 58))
36+
}
37+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
=== tests/cases/compiler/conditionalTypeGenericAssignability.ts ===
2+
function f1<T extends { foo: unknown; 0: unknown }>(_a: T, b: Extract<keyof T, string>) {
3+
>f1 : <T extends { foo: unknown; 0: unknown; }>(_a: T, b: Extract<keyof T, string>) => void
4+
>T : T
5+
>foo : unknown
6+
>0 : unknown
7+
>_a : T
8+
>T : T
9+
>b : Extract<keyof T, string>
10+
>Extract : Extract<T, U>
11+
>T : T
12+
13+
b = "foo"; // succeeds
14+
>b = "foo" : "foo"
15+
>b : Extract<keyof T, string>
16+
>"foo" : "foo"
17+
18+
b = 0; // errors
19+
>b = 0 : 0
20+
>b : Extract<keyof T, string>
21+
>0 : 0
22+
}
23+
24+
function f2<T extends { foo: unknown; 0: unknown }>(_a: T, b: Exclude<keyof T, string>) {
25+
>f2 : <T extends { foo: unknown; 0: unknown; }>(_a: T, b: Exclude<keyof T, string>) => void
26+
>T : T
27+
>foo : unknown
28+
>0 : unknown
29+
>_a : T
30+
>T : T
31+
>b : Exclude<keyof T, string>
32+
>Exclude : Exclude<T, U>
33+
>T : T
34+
35+
b = "foo"; // errors
36+
>b = "foo" : "foo"
37+
>b : Exclude<keyof T, string>
38+
>"foo" : "foo"
39+
40+
b = 0; // succeeds
41+
>b = 0 : 0
42+
>b : Exclude<keyof T, string>
43+
>0 : 0
44+
}
45+

tests/baselines/reference/inferTypes1.types

+1-1
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ type T61<T> = infer A extends infer B ? infer C : infer D; // Error
320320
>D : D
321321

322322
type T62<T> = U extends (infer U)[] ? U : U; // Error
323-
>T62 : any
323+
>T62 : {} | any
324324
>T : T
325325
>U : No type information available!
326326
>U : U
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @strict: true
2+
function f1<T extends { foo: unknown; 0: unknown }>(_a: T, b: Extract<keyof T, string>) {
3+
b = "foo"; // succeeds
4+
b = 0; // errors
5+
}
6+
7+
function f2<T extends { foo: unknown; 0: unknown }>(_a: T, b: Exclude<keyof T, string>) {
8+
b = "foo"; // errors
9+
b = 0; // succeeds
10+
}

0 commit comments

Comments
 (0)