Skip to content

Commit fd1e22b

Browse files
authored
Instantiate generic conditional infer source types in the context of the target conditional (microsoft#31545)
* Instantiate generic conditional infer source types in the context of the target conditional * Add test case from microsoft#26627
1 parent d75de60 commit fd1e22b

5 files changed

+253
-2
lines changed

src/compiler/checker.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -13527,9 +13527,19 @@ namespace ts {
1352713527
// Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if
1352813528
// one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2,
1352913529
// and Y1 is related to Y2.
13530-
if (isTypeIdenticalTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType) &&
13530+
const sourceParams = (source as ConditionalType).root.inferTypeParameters;
13531+
let sourceExtends = (<ConditionalType>source).extendsType;
13532+
let mapper: TypeMapper | undefined;
13533+
if (sourceParams) {
13534+
// If the source has infer type parameters, we instantiate them in the context of the target
13535+
const ctx = createInferenceContext(sourceParams, /*signature*/ undefined, InferenceFlags.None, isRelatedTo);
13536+
inferTypes(ctx.inferences, (<ConditionalType>target).extendsType, sourceExtends, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
13537+
sourceExtends = instantiateType(sourceExtends, ctx.mapper);
13538+
mapper = ctx.mapper;
13539+
}
13540+
if (isTypeIdenticalTo(sourceExtends, (<ConditionalType>target).extendsType) &&
1353113541
(isRelatedTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType) || isRelatedTo((<ConditionalType>target).checkType, (<ConditionalType>source).checkType))) {
13532-
if (result = isRelatedTo(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target), reportErrors)) {
13542+
if (result = isRelatedTo(instantiateType(getTrueTypeFromConditionalType(<ConditionalType>source), mapper), getTrueTypeFromConditionalType(<ConditionalType>target), reportErrors)) {
1353313543
result &= isRelatedTo(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target), reportErrors);
1353413544
}
1353513545
if (result) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//// [identicalGenericConditionalsWithInferRelated.ts]
2+
function f<X>(arg: X) {
3+
type Cond1 = X extends [infer A] ? A : never;
4+
type Cond2 = X extends [infer A] ? A : never;
5+
6+
let x: Cond1 = null as any;
7+
let y: Cond2 = null as any;
8+
x = y; // is err, should be ok
9+
y = x; // is err, should be ok
10+
}
11+
12+
// repro from https://github.com/microsoft/TypeScript/issues/26627
13+
export type Constructor<T> = new (...args: any[]) => T
14+
export type MappedResult<T> =
15+
T extends Boolean ? boolean :
16+
T extends Number ? number :
17+
T extends String ? string :
18+
T
19+
20+
21+
interface X {
22+
decode<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never>
23+
}
24+
25+
class Y implements X {
26+
decode<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never> {
27+
throw new Error()
28+
}
29+
}
30+
31+
32+
//// [identicalGenericConditionalsWithInferRelated.js]
33+
"use strict";
34+
exports.__esModule = true;
35+
function f(arg) {
36+
var x = null;
37+
var y = null;
38+
x = y; // is err, should be ok
39+
y = x; // is err, should be ok
40+
}
41+
var Y = /** @class */ (function () {
42+
function Y() {
43+
}
44+
Y.prototype.decode = function (ctor) {
45+
throw new Error();
46+
};
47+
return Y;
48+
}());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
=== tests/cases/compiler/identicalGenericConditionalsWithInferRelated.ts ===
2+
function f<X>(arg: X) {
3+
>f : Symbol(f, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 0))
4+
>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 11))
5+
>arg : Symbol(arg, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 14))
6+
>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 11))
7+
8+
type Cond1 = X extends [infer A] ? A : never;
9+
>Cond1 : Symbol(Cond1, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 23))
10+
>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 11))
11+
>A : Symbol(A, Decl(identicalGenericConditionalsWithInferRelated.ts, 1, 33))
12+
>A : Symbol(A, Decl(identicalGenericConditionalsWithInferRelated.ts, 1, 33))
13+
14+
type Cond2 = X extends [infer A] ? A : never;
15+
>Cond2 : Symbol(Cond2, Decl(identicalGenericConditionalsWithInferRelated.ts, 1, 49))
16+
>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 11))
17+
>A : Symbol(A, Decl(identicalGenericConditionalsWithInferRelated.ts, 2, 33))
18+
>A : Symbol(A, Decl(identicalGenericConditionalsWithInferRelated.ts, 2, 33))
19+
20+
let x: Cond1 = null as any;
21+
>x : Symbol(x, Decl(identicalGenericConditionalsWithInferRelated.ts, 4, 7))
22+
>Cond1 : Symbol(Cond1, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 23))
23+
24+
let y: Cond2 = null as any;
25+
>y : Symbol(y, Decl(identicalGenericConditionalsWithInferRelated.ts, 5, 7))
26+
>Cond2 : Symbol(Cond2, Decl(identicalGenericConditionalsWithInferRelated.ts, 1, 49))
27+
28+
x = y; // is err, should be ok
29+
>x : Symbol(x, Decl(identicalGenericConditionalsWithInferRelated.ts, 4, 7))
30+
>y : Symbol(y, Decl(identicalGenericConditionalsWithInferRelated.ts, 5, 7))
31+
32+
y = x; // is err, should be ok
33+
>y : Symbol(y, Decl(identicalGenericConditionalsWithInferRelated.ts, 5, 7))
34+
>x : Symbol(x, Decl(identicalGenericConditionalsWithInferRelated.ts, 4, 7))
35+
}
36+
37+
// repro from https://github.com/microsoft/TypeScript/issues/26627
38+
export type Constructor<T> = new (...args: any[]) => T
39+
>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1))
40+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 24))
41+
>args : Symbol(args, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 34))
42+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 24))
43+
44+
export type MappedResult<T> =
45+
>MappedResult : Symbol(MappedResult, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 54))
46+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25))
47+
48+
T extends Boolean ? boolean :
49+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25))
50+
>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
51+
52+
T extends Number ? number :
53+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25))
54+
>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
55+
56+
T extends String ? string :
57+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25))
58+
>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
59+
60+
T
61+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25))
62+
63+
64+
interface X {
65+
>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 16, 5))
66+
67+
decode<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never>
68+
>decode : Symbol(X.decode, Decl(identicalGenericConditionalsWithInferRelated.ts, 19, 13))
69+
>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 11))
70+
>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1))
71+
>ctor : Symbol(ctor, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 39))
72+
>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 11))
73+
>MappedResult : Symbol(MappedResult, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 54))
74+
>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 11))
75+
>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1))
76+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 89))
77+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 89))
78+
}
79+
80+
class Y implements X {
81+
>Y : Symbol(Y, Decl(identicalGenericConditionalsWithInferRelated.ts, 21, 1))
82+
>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 16, 5))
83+
84+
decode<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never> {
85+
>decode : Symbol(Y.decode, Decl(identicalGenericConditionalsWithInferRelated.ts, 23, 22))
86+
>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 11))
87+
>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1))
88+
>ctor : Symbol(ctor, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 39))
89+
>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 11))
90+
>MappedResult : Symbol(MappedResult, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 54))
91+
>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 11))
92+
>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1))
93+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 89))
94+
>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 89))
95+
96+
throw new Error()
97+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
98+
}
99+
}
100+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
=== tests/cases/compiler/identicalGenericConditionalsWithInferRelated.ts ===
2+
function f<X>(arg: X) {
3+
>f : <X>(arg: X) => void
4+
>arg : X
5+
6+
type Cond1 = X extends [infer A] ? A : never;
7+
>Cond1 : X extends [infer A] ? A : never
8+
9+
type Cond2 = X extends [infer A] ? A : never;
10+
>Cond2 : X extends [infer A] ? A : never
11+
12+
let x: Cond1 = null as any;
13+
>x : X extends [infer A] ? A : never
14+
>null as any : any
15+
>null : null
16+
17+
let y: Cond2 = null as any;
18+
>y : X extends [infer A] ? A : never
19+
>null as any : any
20+
>null : null
21+
22+
x = y; // is err, should be ok
23+
>x = y : X extends [infer A] ? A : never
24+
>x : X extends [infer A] ? A : never
25+
>y : X extends [infer A] ? A : never
26+
27+
y = x; // is err, should be ok
28+
>y = x : X extends [infer A] ? A : never
29+
>y : X extends [infer A] ? A : never
30+
>x : X extends [infer A] ? A : never
31+
}
32+
33+
// repro from https://github.com/microsoft/TypeScript/issues/26627
34+
export type Constructor<T> = new (...args: any[]) => T
35+
>Constructor : Constructor<T>
36+
>args : any[]
37+
38+
export type MappedResult<T> =
39+
>MappedResult : MappedResult<T>
40+
41+
T extends Boolean ? boolean :
42+
T extends Number ? number :
43+
T extends String ? string :
44+
T
45+
46+
47+
interface X {
48+
decode<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never>
49+
>decode : <C extends Constructor<any>>(ctor: C) => MappedResult<C extends Constructor<infer T> ? T : never>
50+
>ctor : C
51+
}
52+
53+
class Y implements X {
54+
>Y : Y
55+
56+
decode<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never> {
57+
>decode : <C extends Constructor<any>>(ctor: C) => MappedResult<C extends Constructor<infer T> ? T : never>
58+
>ctor : C
59+
60+
throw new Error()
61+
>new Error() : Error
62+
>Error : ErrorConstructor
63+
}
64+
}
65+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
function f<X>(arg: X) {
2+
type Cond1 = X extends [infer A] ? A : never;
3+
type Cond2 = X extends [infer A] ? A : never;
4+
5+
let x: Cond1 = null as any;
6+
let y: Cond2 = null as any;
7+
x = y; // is err, should be ok
8+
y = x; // is err, should be ok
9+
}
10+
11+
// repro from https://github.com/microsoft/TypeScript/issues/26627
12+
export type Constructor<T> = new (...args: any[]) => T
13+
export type MappedResult<T> =
14+
T extends Boolean ? boolean :
15+
T extends Number ? number :
16+
T extends String ? string :
17+
T
18+
19+
20+
interface X {
21+
decode<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never>
22+
}
23+
24+
class Y implements X {
25+
decode<C extends Constructor<any>>(ctor: C): MappedResult<C extends Constructor<infer T> ? T : never> {
26+
throw new Error()
27+
}
28+
}

0 commit comments

Comments
 (0)