diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f7e36cb1f6..84e3ed842cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13527,9 +13527,19 @@ namespace ts { // Two conditional types 'T1 extends U1 ? X1 : Y1' and 'T2 extends U2 ? X2 : Y2' are related if // one of T1 and T2 is related to the other, U1 and U2 are identical types, X1 is related to X2, // and Y1 is related to Y2. - if (isTypeIdenticalTo((source).extendsType, (target).extendsType) && + const sourceParams = (source as ConditionalType).root.inferTypeParameters; + let sourceExtends = (source).extendsType; + let mapper: TypeMapper | undefined; + if (sourceParams) { + // If the source has infer type parameters, we instantiate them in the context of the target + const ctx = createInferenceContext(sourceParams, /*signature*/ undefined, InferenceFlags.None, isRelatedTo); + inferTypes(ctx.inferences, (target).extendsType, sourceExtends, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict); + sourceExtends = instantiateType(sourceExtends, ctx.mapper); + mapper = ctx.mapper; + } + if (isTypeIdenticalTo(sourceExtends, (target).extendsType) && (isRelatedTo((source).checkType, (target).checkType) || isRelatedTo((target).checkType, (source).checkType))) { - if (result = isRelatedTo(getTrueTypeFromConditionalType(source), getTrueTypeFromConditionalType(target), reportErrors)) { + if (result = isRelatedTo(instantiateType(getTrueTypeFromConditionalType(source), mapper), getTrueTypeFromConditionalType(target), reportErrors)) { result &= isRelatedTo(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target), reportErrors); } if (result) { diff --git a/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.js b/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.js new file mode 100644 index 00000000000..a4bc95d1eb5 --- /dev/null +++ b/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.js @@ -0,0 +1,48 @@ +//// [identicalGenericConditionalsWithInferRelated.ts] +function f(arg: X) { + type Cond1 = X extends [infer A] ? A : never; + type Cond2 = X extends [infer A] ? A : never; + + let x: Cond1 = null as any; + let y: Cond2 = null as any; + x = y; // is err, should be ok + y = x; // is err, should be ok +} + +// repro from https://github.com/microsoft/TypeScript/issues/26627 +export type Constructor = new (...args: any[]) => T +export type MappedResult = + T extends Boolean ? boolean : + T extends Number ? number : + T extends String ? string : + T + + +interface X { + decode>(ctor: C): MappedResult ? T : never> +} + +class Y implements X { + decode>(ctor: C): MappedResult ? T : never> { + throw new Error() + } +} + + +//// [identicalGenericConditionalsWithInferRelated.js] +"use strict"; +exports.__esModule = true; +function f(arg) { + var x = null; + var y = null; + x = y; // is err, should be ok + y = x; // is err, should be ok +} +var Y = /** @class */ (function () { + function Y() { + } + Y.prototype.decode = function (ctor) { + throw new Error(); + }; + return Y; +}()); diff --git a/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.symbols b/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.symbols new file mode 100644 index 00000000000..bfe0e7f8679 --- /dev/null +++ b/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.symbols @@ -0,0 +1,100 @@ +=== tests/cases/compiler/identicalGenericConditionalsWithInferRelated.ts === +function f(arg: X) { +>f : Symbol(f, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 0)) +>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 11)) +>arg : Symbol(arg, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 14)) +>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 11)) + + type Cond1 = X extends [infer A] ? A : never; +>Cond1 : Symbol(Cond1, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 23)) +>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 11)) +>A : Symbol(A, Decl(identicalGenericConditionalsWithInferRelated.ts, 1, 33)) +>A : Symbol(A, Decl(identicalGenericConditionalsWithInferRelated.ts, 1, 33)) + + type Cond2 = X extends [infer A] ? A : never; +>Cond2 : Symbol(Cond2, Decl(identicalGenericConditionalsWithInferRelated.ts, 1, 49)) +>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 11)) +>A : Symbol(A, Decl(identicalGenericConditionalsWithInferRelated.ts, 2, 33)) +>A : Symbol(A, Decl(identicalGenericConditionalsWithInferRelated.ts, 2, 33)) + + let x: Cond1 = null as any; +>x : Symbol(x, Decl(identicalGenericConditionalsWithInferRelated.ts, 4, 7)) +>Cond1 : Symbol(Cond1, Decl(identicalGenericConditionalsWithInferRelated.ts, 0, 23)) + + let y: Cond2 = null as any; +>y : Symbol(y, Decl(identicalGenericConditionalsWithInferRelated.ts, 5, 7)) +>Cond2 : Symbol(Cond2, Decl(identicalGenericConditionalsWithInferRelated.ts, 1, 49)) + + x = y; // is err, should be ok +>x : Symbol(x, Decl(identicalGenericConditionalsWithInferRelated.ts, 4, 7)) +>y : Symbol(y, Decl(identicalGenericConditionalsWithInferRelated.ts, 5, 7)) + + y = x; // is err, should be ok +>y : Symbol(y, Decl(identicalGenericConditionalsWithInferRelated.ts, 5, 7)) +>x : Symbol(x, Decl(identicalGenericConditionalsWithInferRelated.ts, 4, 7)) +} + +// repro from https://github.com/microsoft/TypeScript/issues/26627 +export type Constructor = new (...args: any[]) => T +>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1)) +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 24)) +>args : Symbol(args, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 34)) +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 24)) + +export type MappedResult = +>MappedResult : Symbol(MappedResult, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 54)) +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25)) + + T extends Boolean ? boolean : +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25)) +>Boolean : Symbol(Boolean, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + T extends Number ? number : +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + T extends String ? string : +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25)) +>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + T +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 12, 25)) + + +interface X { +>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 16, 5)) + + decode>(ctor: C): MappedResult ? T : never> +>decode : Symbol(X.decode, Decl(identicalGenericConditionalsWithInferRelated.ts, 19, 13)) +>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 11)) +>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1)) +>ctor : Symbol(ctor, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 39)) +>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 11)) +>MappedResult : Symbol(MappedResult, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 54)) +>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 11)) +>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1)) +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 89)) +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 20, 89)) +} + +class Y implements X { +>Y : Symbol(Y, Decl(identicalGenericConditionalsWithInferRelated.ts, 21, 1)) +>X : Symbol(X, Decl(identicalGenericConditionalsWithInferRelated.ts, 16, 5)) + + decode>(ctor: C): MappedResult ? T : never> { +>decode : Symbol(Y.decode, Decl(identicalGenericConditionalsWithInferRelated.ts, 23, 22)) +>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 11)) +>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1)) +>ctor : Symbol(ctor, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 39)) +>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 11)) +>MappedResult : Symbol(MappedResult, Decl(identicalGenericConditionalsWithInferRelated.ts, 11, 54)) +>C : Symbol(C, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 11)) +>Constructor : Symbol(Constructor, Decl(identicalGenericConditionalsWithInferRelated.ts, 8, 1)) +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 89)) +>T : Symbol(T, Decl(identicalGenericConditionalsWithInferRelated.ts, 24, 89)) + + throw new Error() +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + } +} + diff --git a/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.types b/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.types new file mode 100644 index 00000000000..c2ec649397d --- /dev/null +++ b/tests/baselines/reference/identicalGenericConditionalsWithInferRelated.types @@ -0,0 +1,65 @@ +=== tests/cases/compiler/identicalGenericConditionalsWithInferRelated.ts === +function f(arg: X) { +>f : (arg: X) => void +>arg : X + + type Cond1 = X extends [infer A] ? A : never; +>Cond1 : X extends [infer A] ? A : never + + type Cond2 = X extends [infer A] ? A : never; +>Cond2 : X extends [infer A] ? A : never + + let x: Cond1 = null as any; +>x : X extends [infer A] ? A : never +>null as any : any +>null : null + + let y: Cond2 = null as any; +>y : X extends [infer A] ? A : never +>null as any : any +>null : null + + x = y; // is err, should be ok +>x = y : X extends [infer A] ? A : never +>x : X extends [infer A] ? A : never +>y : X extends [infer A] ? A : never + + y = x; // is err, should be ok +>y = x : X extends [infer A] ? A : never +>y : X extends [infer A] ? A : never +>x : X extends [infer A] ? A : never +} + +// repro from https://github.com/microsoft/TypeScript/issues/26627 +export type Constructor = new (...args: any[]) => T +>Constructor : Constructor +>args : any[] + +export type MappedResult = +>MappedResult : MappedResult + + T extends Boolean ? boolean : + T extends Number ? number : + T extends String ? string : + T + + +interface X { + decode>(ctor: C): MappedResult ? T : never> +>decode : >(ctor: C) => MappedResult ? T : never> +>ctor : C +} + +class Y implements X { +>Y : Y + + decode>(ctor: C): MappedResult ? T : never> { +>decode : >(ctor: C) => MappedResult ? T : never> +>ctor : C + + throw new Error() +>new Error() : Error +>Error : ErrorConstructor + } +} + diff --git a/tests/cases/compiler/identicalGenericConditionalsWithInferRelated.ts b/tests/cases/compiler/identicalGenericConditionalsWithInferRelated.ts new file mode 100644 index 00000000000..1c7e80780e5 --- /dev/null +++ b/tests/cases/compiler/identicalGenericConditionalsWithInferRelated.ts @@ -0,0 +1,28 @@ +function f(arg: X) { + type Cond1 = X extends [infer A] ? A : never; + type Cond2 = X extends [infer A] ? A : never; + + let x: Cond1 = null as any; + let y: Cond2 = null as any; + x = y; // is err, should be ok + y = x; // is err, should be ok +} + +// repro from https://github.com/microsoft/TypeScript/issues/26627 +export type Constructor = new (...args: any[]) => T +export type MappedResult = + T extends Boolean ? boolean : + T extends Number ? number : + T extends String ? string : + T + + +interface X { + decode>(ctor: C): MappedResult ? T : never> +} + +class Y implements X { + decode>(ctor: C): MappedResult ? T : never> { + throw new Error() + } +}