From 6b04aeffd5f4c52ac10f85e8504ab64c1990fef4 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 30 Nov 2023 14:29:28 -0800 Subject: [PATCH 1/5] add test --- ...mappedTypeInferenceToMappedType.errors.txt | 24 +++++++++++ .../mappedTypeInferenceToMappedType.symbols | 42 +++++++++++++++++++ .../mappedTypeInferenceToMappedType.types | 24 +++++++++++ .../mappedTypeInferenceToMappedType.ts | 13 ++++++ 4 files changed, 103 insertions(+) create mode 100644 tests/baselines/reference/mappedTypeInferenceToMappedType.errors.txt create mode 100644 tests/baselines/reference/mappedTypeInferenceToMappedType.symbols create mode 100644 tests/baselines/reference/mappedTypeInferenceToMappedType.types create mode 100644 tests/cases/compiler/mappedTypeInferenceToMappedType.ts diff --git a/tests/baselines/reference/mappedTypeInferenceToMappedType.errors.txt b/tests/baselines/reference/mappedTypeInferenceToMappedType.errors.txt new file mode 100644 index 0000000000000..e691652a75a4c --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceToMappedType.errors.txt @@ -0,0 +1,24 @@ +mappedTypeInferenceToMappedType.ts(9,5): error TS2416: Property 'method' in type 'Derived' is not assignable to the same property in base type 'Base'. + Type '(x: { [K in keyof U]: U[K]; }) => Base' is not assignable to type '(x: { [K in keyof U]: U[K]; }) => Base'. Two different types with this name exist, but they are unrelated. + Type 'Base' is not assignable to type 'Base'. + Type 'unknown[]' is not assignable to type 'U'. + 'unknown[]' is assignable to the constraint of type 'U', but 'U' could be instantiated with a different subtype of constraint 'unknown[]'. + + +==== mappedTypeInferenceToMappedType.ts (1 errors) ==== + // #56133 + + declare class Base { + someProp: T; + method(x: { [K in keyof U]: U[K] }): Base; + } + + declare class Derived extends Base { + method(x: { [K in keyof U]: U[K] }): Base; + ~~~~~~ +!!! error TS2416: Property 'method' in type 'Derived' is not assignable to the same property in base type 'Base'. +!!! error TS2416: Type '(x: { [K in keyof U]: U[K]; }) => Base' is not assignable to type '(x: { [K in keyof U]: U[K]; }) => Base'. Two different types with this name exist, but they are unrelated. +!!! error TS2416: Type 'Base' is not assignable to type 'Base'. +!!! error TS2416: Type 'unknown[]' is not assignable to type 'U'. +!!! error TS2416: 'unknown[]' is assignable to the constraint of type 'U', but 'U' could be instantiated with a different subtype of constraint 'unknown[]'. + } \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeInferenceToMappedType.symbols b/tests/baselines/reference/mappedTypeInferenceToMappedType.symbols new file mode 100644 index 0000000000000..41ee7c009d275 --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceToMappedType.symbols @@ -0,0 +1,42 @@ +//// [tests/cases/compiler/mappedTypeInferenceToMappedType.ts] //// + +=== mappedTypeInferenceToMappedType.ts === +// #56133 + +declare class Base { +>Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 2, 19)) + + someProp: T; +>someProp : Symbol(Base.someProp, Decl(mappedTypeInferenceToMappedType.ts, 2, 23)) +>T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 2, 19)) + + method(x: { [K in keyof U]: U[K] }): Base; +>method : Symbol(Base.method, Decl(mappedTypeInferenceToMappedType.ts, 3, 16)) +>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 4, 11)) +>x : Symbol(x, Decl(mappedTypeInferenceToMappedType.ts, 4, 32)) +>K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 4, 38)) +>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 4, 11)) +>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 4, 11)) +>K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 4, 38)) +>Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0)) +>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 4, 11)) +} + +declare class Derived extends Base { +>Derived : Symbol(Derived, Decl(mappedTypeInferenceToMappedType.ts, 5, 1)) +>T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 7, 22)) +>Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 7, 22)) + + method(x: { [K in keyof U]: U[K] }): Base; +>method : Symbol(Derived.method, Decl(mappedTypeInferenceToMappedType.ts, 7, 42)) +>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) +>x : Symbol(x, Decl(mappedTypeInferenceToMappedType.ts, 8, 32)) +>K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 8, 38)) +>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) +>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) +>K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 8, 38)) +>Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0)) +>U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) +} diff --git a/tests/baselines/reference/mappedTypeInferenceToMappedType.types b/tests/baselines/reference/mappedTypeInferenceToMappedType.types new file mode 100644 index 0000000000000..931124d958ec2 --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceToMappedType.types @@ -0,0 +1,24 @@ +//// [tests/cases/compiler/mappedTypeInferenceToMappedType.ts] //// + +=== mappedTypeInferenceToMappedType.ts === +// #56133 + +declare class Base { +>Base : Base + + someProp: T; +>someProp : T + + method(x: { [K in keyof U]: U[K] }): Base; +>method : (x: { [K in keyof U]: U[K]; }) => Base +>x : { [K in keyof U]: U[K]; } +} + +declare class Derived extends Base { +>Derived : Derived +>Base : Base + + method(x: { [K in keyof U]: U[K] }): Base; +>method : (x: { [K in keyof U]: U[K]; }) => Base +>x : { [K in keyof U]: U[K]; } +} diff --git a/tests/cases/compiler/mappedTypeInferenceToMappedType.ts b/tests/cases/compiler/mappedTypeInferenceToMappedType.ts new file mode 100644 index 0000000000000..7b12e4d79f9a7 --- /dev/null +++ b/tests/cases/compiler/mappedTypeInferenceToMappedType.ts @@ -0,0 +1,13 @@ +// @strict: true +// @noEmit: true + +// #56133 + +declare class Base { + someProp: T; + method(x: { [K in keyof U]: U[K] }): Base; +} + +declare class Derived extends Base { + method(x: { [K in keyof U]: U[K] }): Base; +} \ No newline at end of file From 47cb9bc4ad05c23720a1ea947d587f364bf2be6f Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 30 Nov 2023 18:34:22 -0800 Subject: [PATCH 2/5] add regression test --- ...appedTypeInferenceFromApparentType.symbols | 44 +++++++++++++++++++ .../mappedTypeInferenceFromApparentType.types | 33 ++++++++++++++ .../mappedTypeInferenceFromApparentType.ts | 14 ++++++ 3 files changed, 91 insertions(+) create mode 100644 tests/baselines/reference/mappedTypeInferenceFromApparentType.symbols create mode 100644 tests/baselines/reference/mappedTypeInferenceFromApparentType.types create mode 100644 tests/cases/compiler/mappedTypeInferenceFromApparentType.ts diff --git a/tests/baselines/reference/mappedTypeInferenceFromApparentType.symbols b/tests/baselines/reference/mappedTypeInferenceFromApparentType.symbols new file mode 100644 index 0000000000000..3d063329b80a5 --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceFromApparentType.symbols @@ -0,0 +1,44 @@ +//// [tests/cases/compiler/mappedTypeInferenceFromApparentType.ts] //// + +=== mappedTypeInferenceFromApparentType.ts === +type Obj = { +>Obj : Symbol(Obj, Decl(mappedTypeInferenceFromApparentType.ts, 0, 0)) + + [s: string]: number; +>s : Symbol(s, Decl(mappedTypeInferenceFromApparentType.ts, 1, 5)) + + [u: symbol]: boolean; +>u : Symbol(u, Decl(mappedTypeInferenceFromApparentType.ts, 2, 5)) + +}; + +type foo = (target: { [K in keyof T]: T[K] }) => void; +>foo : Symbol(foo, Decl(mappedTypeInferenceFromApparentType.ts, 3, 2)) +>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12)) +>target : Symbol(target, Decl(mappedTypeInferenceFromApparentType.ts, 5, 15)) +>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 5, 26)) +>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12)) +>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12)) +>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 5, 26)) + +type bar = (source: { [K in keyof U]: Obj[K] }) => void; +>bar : Symbol(bar, Decl(mappedTypeInferenceFromApparentType.ts, 5, 57)) +>U : Symbol(U, Decl(mappedTypeInferenceFromApparentType.ts, 6, 12)) +>source : Symbol(source, Decl(mappedTypeInferenceFromApparentType.ts, 6, 32)) +>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 6, 43)) +>U : Symbol(U, Decl(mappedTypeInferenceFromApparentType.ts, 6, 12)) +>Obj : Symbol(Obj, Decl(mappedTypeInferenceFromApparentType.ts, 0, 0)) +>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 6, 43)) + +declare let f: foo; +>f : Symbol(f, Decl(mappedTypeInferenceFromApparentType.ts, 8, 11)) +>foo : Symbol(foo, Decl(mappedTypeInferenceFromApparentType.ts, 3, 2)) + +declare let b: bar; +>b : Symbol(b, Decl(mappedTypeInferenceFromApparentType.ts, 9, 11)) +>bar : Symbol(bar, Decl(mappedTypeInferenceFromApparentType.ts, 5, 57)) + +b = f; +>b : Symbol(b, Decl(mappedTypeInferenceFromApparentType.ts, 9, 11)) +>f : Symbol(f, Decl(mappedTypeInferenceFromApparentType.ts, 8, 11)) + diff --git a/tests/baselines/reference/mappedTypeInferenceFromApparentType.types b/tests/baselines/reference/mappedTypeInferenceFromApparentType.types new file mode 100644 index 0000000000000..a35f16c3b64f5 --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceFromApparentType.types @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/mappedTypeInferenceFromApparentType.ts] //// + +=== mappedTypeInferenceFromApparentType.ts === +type Obj = { +>Obj : { [s: string]: number; [u: symbol]: boolean; } + + [s: string]: number; +>s : string + + [u: symbol]: boolean; +>u : symbol + +}; + +type foo = (target: { [K in keyof T]: T[K] }) => void; +>foo : (target: { [K in keyof T]: T[K]; }) => void +>target : { [K in keyof T]: T[K]; } + +type bar = (source: { [K in keyof U]: Obj[K] }) => void; +>bar : (source: { [K in keyof U]: Obj[K]; }) => void +>source : { [K in keyof U]: Obj[K]; } + +declare let f: foo; +>f : foo + +declare let b: bar; +>b : bar + +b = f; +>b = f : foo +>b : bar +>f : foo + diff --git a/tests/cases/compiler/mappedTypeInferenceFromApparentType.ts b/tests/cases/compiler/mappedTypeInferenceFromApparentType.ts new file mode 100644 index 0000000000000..5b55f1b6787b3 --- /dev/null +++ b/tests/cases/compiler/mappedTypeInferenceFromApparentType.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +type Obj = { + [s: string]: number; + [u: symbol]: boolean; +}; + +type foo = (target: { [K in keyof T]: T[K] }) => void; +type bar = (source: { [K in keyof U]: Obj[K] }) => void; + +declare let f: foo; +declare let b: bar; +b = f; \ No newline at end of file From 88f7f1293863fa472c6bac5ee74d8c60471f60a9 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 1 Dec 2023 13:59:55 -0800 Subject: [PATCH 3/5] breaking change --- src/compiler/checker.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4cd242f264b1a..d2a1d630adbcb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25256,6 +25256,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inferToTemplateLiteralType(source, target as TemplateLiteralType); } else { + if (isGenericMappedType(source) && isGenericMappedType(target)) { + invokeOnce(source, target, (source, target) => { + // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer + // from S to T and from X to Y. + inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target)); + inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); + const sourceNameType = getNameTypeFromMappedType(source); + const targetNameType = getNameTypeFromMappedType(target); + if (sourceNameType && targetNameType) inferFromTypes(sourceNameType, targetNameType); + }); + } source = getReducedType(source); if (!(priority & InferencePriority.NoConstraints && source.flags & (TypeFlags.Intersection | TypeFlags.Instantiable))) { const apparentSource = getApparentType(source); From 4dcc69af8ba329a1e70759ec18a3d281f91baa1f Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 12 Dec 2023 17:12:57 -0800 Subject: [PATCH 4/5] cleanup --- src/compiler/checker.ts | 30 ++++++-------- ...edTypeInferenceFromApparentType.errors.txt | 26 ++++++++++++ ...appedTypeInferenceFromApparentType.symbols | 41 +++++++++---------- .../mappedTypeInferenceFromApparentType.types | 5 +-- ...mappedTypeInferenceToMappedType.errors.txt | 24 ----------- .../mappedTypeInferenceToMappedType.symbols | 10 ++--- .../mappedTypeInferenceToMappedType.types | 6 +-- .../mappedTypeInferenceFromApparentType.ts | 1 - .../mappedTypeInferenceToMappedType.ts | 2 +- 9 files changed, 68 insertions(+), 77 deletions(-) create mode 100644 tests/baselines/reference/mappedTypeInferenceFromApparentType.errors.txt delete mode 100644 tests/baselines/reference/mappedTypeInferenceToMappedType.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d2a1d630adbcb..d42a14b493594 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25256,18 +25256,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inferToTemplateLiteralType(source, target as TemplateLiteralType); } else { + source = getReducedType(source); if (isGenericMappedType(source) && isGenericMappedType(target)) { - invokeOnce(source, target, (source, target) => { - // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer - // from S to T and from X to Y. - inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target)); - inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); - const sourceNameType = getNameTypeFromMappedType(source); - const targetNameType = getNameTypeFromMappedType(target); - if (sourceNameType && targetNameType) inferFromTypes(sourceNameType, targetNameType); - }); + invokeOnce(source, target, inferFromGenericMappedTypes); } - source = getReducedType(source); if (!(priority & InferencePriority.NoConstraints && source.flags & (TypeFlags.Intersection | TypeFlags.Instantiable))) { const apparentSource = getApparentType(source); // getApparentType can return _any_ type, since an indexed access or conditional may simplify to any other type. @@ -25612,6 +25604,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function inferFromGenericMappedTypes(source: MappedType, target: MappedType) { + // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer + // from S to T and from X to Y. + inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target)); + inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); + const sourceNameType = getNameTypeFromMappedType(source); + const targetNameType = getNameTypeFromMappedType(target); + if (sourceNameType && targetNameType) inferFromTypes(sourceNameType, targetNameType); + } + function inferFromObjectTypes(source: Type, target: Type) { if ( getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && ( @@ -25623,13 +25625,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return; } if (isGenericMappedType(source) && isGenericMappedType(target)) { - // The source and target types are generic types { [P in S]: X } and { [P in T]: Y }, so we infer - // from S to T and from X to Y. - inferFromTypes(getConstraintTypeFromMappedType(source), getConstraintTypeFromMappedType(target)); - inferFromTypes(getTemplateTypeFromMappedType(source), getTemplateTypeFromMappedType(target)); - const sourceNameType = getNameTypeFromMappedType(source); - const targetNameType = getNameTypeFromMappedType(target); - if (sourceNameType && targetNameType) inferFromTypes(sourceNameType, targetNameType); + inferFromGenericMappedTypes(source, target); } if (getObjectFlags(target) & ObjectFlags.Mapped && !(target as MappedType).declaration.nameType) { const constraintType = getConstraintTypeFromMappedType(target as MappedType); diff --git a/tests/baselines/reference/mappedTypeInferenceFromApparentType.errors.txt b/tests/baselines/reference/mappedTypeInferenceFromApparentType.errors.txt new file mode 100644 index 0000000000000..f61f0d58bcb99 --- /dev/null +++ b/tests/baselines/reference/mappedTypeInferenceFromApparentType.errors.txt @@ -0,0 +1,26 @@ +mappedTypeInferenceFromApparentType.ts(10,1): error TS2322: Type 'foo' is not assignable to type 'bar'. + Types of parameters 'target' and 'source' are incompatible. + Type '{ [K in keyof U]: Obj[K]; }' is not assignable to type '{ [K in keyof U]: U[K]; }'. + Type 'Obj[K]' is not assignable to type 'U[K]'. + Type 'Obj' is not assignable to type 'U'. + 'U' could be instantiated with an arbitrary type which could be unrelated to 'Obj'. + + +==== mappedTypeInferenceFromApparentType.ts (1 errors) ==== + type Obj = { + [s: string]: number; + }; + + type foo = (target: { [K in keyof T]: T[K] }) => void; + type bar = (source: { [K in keyof U]: Obj[K] }) => void; + + declare let f: foo; + declare let b: bar; + b = f; + ~ +!!! error TS2322: Type 'foo' is not assignable to type 'bar'. +!!! error TS2322: Types of parameters 'target' and 'source' are incompatible. +!!! error TS2322: Type '{ [K in keyof U]: Obj[K]; }' is not assignable to type '{ [K in keyof U]: U[K]; }'. +!!! error TS2322: Type 'Obj[K]' is not assignable to type 'U[K]'. +!!! error TS2322: Type 'Obj' is not assignable to type 'U'. +!!! error TS2322: 'U' could be instantiated with an arbitrary type which could be unrelated to 'Obj'. \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeInferenceFromApparentType.symbols b/tests/baselines/reference/mappedTypeInferenceFromApparentType.symbols index 3d063329b80a5..3f76c44db9d59 100644 --- a/tests/baselines/reference/mappedTypeInferenceFromApparentType.symbols +++ b/tests/baselines/reference/mappedTypeInferenceFromApparentType.symbols @@ -7,38 +7,35 @@ type Obj = { [s: string]: number; >s : Symbol(s, Decl(mappedTypeInferenceFromApparentType.ts, 1, 5)) - [u: symbol]: boolean; ->u : Symbol(u, Decl(mappedTypeInferenceFromApparentType.ts, 2, 5)) - }; type foo = (target: { [K in keyof T]: T[K] }) => void; ->foo : Symbol(foo, Decl(mappedTypeInferenceFromApparentType.ts, 3, 2)) ->T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12)) ->target : Symbol(target, Decl(mappedTypeInferenceFromApparentType.ts, 5, 15)) ->K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 5, 26)) ->T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12)) ->T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12)) ->K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 5, 26)) +>foo : Symbol(foo, Decl(mappedTypeInferenceFromApparentType.ts, 2, 2)) +>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 4, 12)) +>target : Symbol(target, Decl(mappedTypeInferenceFromApparentType.ts, 4, 15)) +>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 4, 26)) +>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 4, 12)) +>T : Symbol(T, Decl(mappedTypeInferenceFromApparentType.ts, 4, 12)) +>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 4, 26)) type bar = (source: { [K in keyof U]: Obj[K] }) => void; ->bar : Symbol(bar, Decl(mappedTypeInferenceFromApparentType.ts, 5, 57)) ->U : Symbol(U, Decl(mappedTypeInferenceFromApparentType.ts, 6, 12)) ->source : Symbol(source, Decl(mappedTypeInferenceFromApparentType.ts, 6, 32)) ->K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 6, 43)) ->U : Symbol(U, Decl(mappedTypeInferenceFromApparentType.ts, 6, 12)) +>bar : Symbol(bar, Decl(mappedTypeInferenceFromApparentType.ts, 4, 57)) +>U : Symbol(U, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12)) +>source : Symbol(source, Decl(mappedTypeInferenceFromApparentType.ts, 5, 32)) +>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 5, 43)) +>U : Symbol(U, Decl(mappedTypeInferenceFromApparentType.ts, 5, 12)) >Obj : Symbol(Obj, Decl(mappedTypeInferenceFromApparentType.ts, 0, 0)) ->K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 6, 43)) +>K : Symbol(K, Decl(mappedTypeInferenceFromApparentType.ts, 5, 43)) declare let f: foo; ->f : Symbol(f, Decl(mappedTypeInferenceFromApparentType.ts, 8, 11)) ->foo : Symbol(foo, Decl(mappedTypeInferenceFromApparentType.ts, 3, 2)) +>f : Symbol(f, Decl(mappedTypeInferenceFromApparentType.ts, 7, 11)) +>foo : Symbol(foo, Decl(mappedTypeInferenceFromApparentType.ts, 2, 2)) declare let b: bar; ->b : Symbol(b, Decl(mappedTypeInferenceFromApparentType.ts, 9, 11)) ->bar : Symbol(bar, Decl(mappedTypeInferenceFromApparentType.ts, 5, 57)) +>b : Symbol(b, Decl(mappedTypeInferenceFromApparentType.ts, 8, 11)) +>bar : Symbol(bar, Decl(mappedTypeInferenceFromApparentType.ts, 4, 57)) b = f; ->b : Symbol(b, Decl(mappedTypeInferenceFromApparentType.ts, 9, 11)) ->f : Symbol(f, Decl(mappedTypeInferenceFromApparentType.ts, 8, 11)) +>b : Symbol(b, Decl(mappedTypeInferenceFromApparentType.ts, 8, 11)) +>f : Symbol(f, Decl(mappedTypeInferenceFromApparentType.ts, 7, 11)) diff --git a/tests/baselines/reference/mappedTypeInferenceFromApparentType.types b/tests/baselines/reference/mappedTypeInferenceFromApparentType.types index a35f16c3b64f5..41705b518d68e 100644 --- a/tests/baselines/reference/mappedTypeInferenceFromApparentType.types +++ b/tests/baselines/reference/mappedTypeInferenceFromApparentType.types @@ -2,14 +2,11 @@ === mappedTypeInferenceFromApparentType.ts === type Obj = { ->Obj : { [s: string]: number; [u: symbol]: boolean; } +>Obj : { [s: string]: number; } [s: string]: number; >s : string - [u: symbol]: boolean; ->u : symbol - }; type foo = (target: { [K in keyof T]: T[K] }) => void; diff --git a/tests/baselines/reference/mappedTypeInferenceToMappedType.errors.txt b/tests/baselines/reference/mappedTypeInferenceToMappedType.errors.txt deleted file mode 100644 index e691652a75a4c..0000000000000 --- a/tests/baselines/reference/mappedTypeInferenceToMappedType.errors.txt +++ /dev/null @@ -1,24 +0,0 @@ -mappedTypeInferenceToMappedType.ts(9,5): error TS2416: Property 'method' in type 'Derived' is not assignable to the same property in base type 'Base'. - Type '(x: { [K in keyof U]: U[K]; }) => Base' is not assignable to type '(x: { [K in keyof U]: U[K]; }) => Base'. Two different types with this name exist, but they are unrelated. - Type 'Base' is not assignable to type 'Base'. - Type 'unknown[]' is not assignable to type 'U'. - 'unknown[]' is assignable to the constraint of type 'U', but 'U' could be instantiated with a different subtype of constraint 'unknown[]'. - - -==== mappedTypeInferenceToMappedType.ts (1 errors) ==== - // #56133 - - declare class Base { - someProp: T; - method(x: { [K in keyof U]: U[K] }): Base; - } - - declare class Derived extends Base { - method(x: { [K in keyof U]: U[K] }): Base; - ~~~~~~ -!!! error TS2416: Property 'method' in type 'Derived' is not assignable to the same property in base type 'Base'. -!!! error TS2416: Type '(x: { [K in keyof U]: U[K]; }) => Base' is not assignable to type '(x: { [K in keyof U]: U[K]; }) => Base'. Two different types with this name exist, but they are unrelated. -!!! error TS2416: Type 'Base' is not assignable to type 'Base'. -!!! error TS2416: Type 'unknown[]' is not assignable to type 'U'. -!!! error TS2416: 'unknown[]' is assignable to the constraint of type 'U', but 'U' could be instantiated with a different subtype of constraint 'unknown[]'. - } \ No newline at end of file diff --git a/tests/baselines/reference/mappedTypeInferenceToMappedType.symbols b/tests/baselines/reference/mappedTypeInferenceToMappedType.symbols index 41ee7c009d275..b1f19eadcb01f 100644 --- a/tests/baselines/reference/mappedTypeInferenceToMappedType.symbols +++ b/tests/baselines/reference/mappedTypeInferenceToMappedType.symbols @@ -29,14 +29,14 @@ declare class Derived extends Base { >Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0)) >T : Symbol(T, Decl(mappedTypeInferenceToMappedType.ts, 7, 22)) - method(x: { [K in keyof U]: U[K] }): Base; + method(x: { [K in keyof V]: V[K] }): Base; >method : Symbol(Derived.method, Decl(mappedTypeInferenceToMappedType.ts, 7, 42)) ->U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) +>V : Symbol(V, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) >x : Symbol(x, Decl(mappedTypeInferenceToMappedType.ts, 8, 32)) >K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 8, 38)) ->U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) ->U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) +>V : Symbol(V, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) +>V : Symbol(V, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) >K : Symbol(K, Decl(mappedTypeInferenceToMappedType.ts, 8, 38)) >Base : Symbol(Base, Decl(mappedTypeInferenceToMappedType.ts, 0, 0)) ->U : Symbol(U, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) +>V : Symbol(V, Decl(mappedTypeInferenceToMappedType.ts, 8, 11)) } diff --git a/tests/baselines/reference/mappedTypeInferenceToMappedType.types b/tests/baselines/reference/mappedTypeInferenceToMappedType.types index 931124d958ec2..9474e8de6fb98 100644 --- a/tests/baselines/reference/mappedTypeInferenceToMappedType.types +++ b/tests/baselines/reference/mappedTypeInferenceToMappedType.types @@ -18,7 +18,7 @@ declare class Derived extends Base { >Derived : Derived >Base : Base - method(x: { [K in keyof U]: U[K] }): Base; ->method : (x: { [K in keyof U]: U[K]; }) => Base ->x : { [K in keyof U]: U[K]; } + method(x: { [K in keyof V]: V[K] }): Base; +>method : (x: { [K in keyof V]: V[K]; }) => Base +>x : { [K in keyof V]: V[K]; } } diff --git a/tests/cases/compiler/mappedTypeInferenceFromApparentType.ts b/tests/cases/compiler/mappedTypeInferenceFromApparentType.ts index 5b55f1b6787b3..9fc5e6401a72f 100644 --- a/tests/cases/compiler/mappedTypeInferenceFromApparentType.ts +++ b/tests/cases/compiler/mappedTypeInferenceFromApparentType.ts @@ -3,7 +3,6 @@ type Obj = { [s: string]: number; - [u: symbol]: boolean; }; type foo = (target: { [K in keyof T]: T[K] }) => void; diff --git a/tests/cases/compiler/mappedTypeInferenceToMappedType.ts b/tests/cases/compiler/mappedTypeInferenceToMappedType.ts index 7b12e4d79f9a7..2bf82fac1c78a 100644 --- a/tests/cases/compiler/mappedTypeInferenceToMappedType.ts +++ b/tests/cases/compiler/mappedTypeInferenceToMappedType.ts @@ -9,5 +9,5 @@ declare class Base { } declare class Derived extends Base { - method(x: { [K in keyof U]: U[K] }): Base; + method(x: { [K in keyof V]: V[K] }): Base; } \ No newline at end of file From d867225196d6ef1afaa61c767c02f483f3f16331 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 13 Dec 2023 16:36:01 -0800 Subject: [PATCH 5/5] add documentation to `invokeOnce` --- src/compiler/checker.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d42a14b493594..e1f7b267ba50b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25297,6 +25297,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { priority = savePriority; } + // Ensure an inference action is performed only once for the given source and target types. + // This includes two things: + // Avoiding inferring between the same pair of source and target types, + // and avoiding circularly inferring between source and target types. + // For an example of the last, consider if we are inferring between source type + // `type Deep = { next: Deep> }` and target type `type Loop = { next: Loop }`. + // We would then infer between the types of the `next` property: `Deep>` = `{ next: Deep>> }` and `Loop` = `{ next: Loop }`. + // We will then infer again between the types of the `next` property: + // `Deep>>` and `Loop`, and so on, such that we would be forever inferring + // between instantiations of the same types `Deep` and `Loop`. + // In particular, we would be inferring from increasingly deep instantiations of `Deep` to `Loop`, + // such that we would go on inferring forever, even though we would never infer + // between the same pair of types. function invokeOnce(source: Source, target: Target, action: (source: Source, target: Target) => void) { const key = source.id + "," + target.id; const status = visited && visited.get(key);