From 63d746bc4c5f3cee42e65462705a038e4a7412fa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg <andersh@microsoft.com> Date: Mon, 11 Sep 2017 10:24:27 -0700 Subject: [PATCH 1/2] Higher order inference for mapped, index and lookup types --- src/compiler/checker.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ff5d1c661e866..4f2f065f4f315 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10386,7 +10386,7 @@ namespace ts { // results for union and intersection types for performance reasons. function couldContainTypeVariables(type: Type): boolean { const objectFlags = getObjectFlags(type); - return !!(type.flags & TypeFlags.TypeVariable || + return !!(type.flags & (TypeFlags.TypeVariable | TypeFlags.Index) || objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeVariables) || objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) || objectFlags & ObjectFlags.Mapped || @@ -10554,6 +10554,13 @@ namespace ts { inferFromTypes(sourceTypes[i], targetTypes[i]); } } + else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) { + inferFromTypes((<IndexType>source).type, (<IndexType>target).type); + } + else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) { + inferFromTypes((<IndexedAccessType>source).objectType, (<IndexedAccessType>target).objectType); + inferFromTypes((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType); + } else if (target.flags & TypeFlags.UnionOrIntersection) { const targetTypes = (<UnionOrIntersectionType>target).types; let typeVariableCount = 0; @@ -10627,6 +10634,12 @@ namespace ts { } function inferFromObjectTypes(source: Type, target: Type) { + 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(<MappedType>source), getConstraintTypeFromMappedType(<MappedType>target)); + inferFromTypes(getTemplateTypeFromMappedType(<MappedType>source), getTemplateTypeFromMappedType(<MappedType>target)); + } if (getObjectFlags(target) & ObjectFlags.Mapped) { const constraintType = getConstraintTypeFromMappedType(<MappedType>target); if (constraintType.flags & TypeFlags.Index) { From 0823eba8a3257b80df68494ac0d534ac9b6bd16b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg <andersh@microsoft.com> Date: Mon, 11 Sep 2017 10:38:46 -0700 Subject: [PATCH 2/2] Add tests --- .../higherOrderMappedIndexLookupInference.js | 43 ++++++++ ...herOrderMappedIndexLookupInference.symbols | 98 +++++++++++++++++ ...igherOrderMappedIndexLookupInference.types | 104 ++++++++++++++++++ .../higherOrderMappedIndexLookupInference.ts | 25 +++++ 4 files changed, 270 insertions(+) create mode 100644 tests/baselines/reference/higherOrderMappedIndexLookupInference.js create mode 100644 tests/baselines/reference/higherOrderMappedIndexLookupInference.symbols create mode 100644 tests/baselines/reference/higherOrderMappedIndexLookupInference.types create mode 100644 tests/cases/compiler/higherOrderMappedIndexLookupInference.ts diff --git a/tests/baselines/reference/higherOrderMappedIndexLookupInference.js b/tests/baselines/reference/higherOrderMappedIndexLookupInference.js new file mode 100644 index 0000000000000..e8e09a59713cb --- /dev/null +++ b/tests/baselines/reference/higherOrderMappedIndexLookupInference.js @@ -0,0 +1,43 @@ +//// [higherOrderMappedIndexLookupInference.ts] +// @strict + +function f1(a: <T>() => keyof T, b: <U>() => keyof U) { + a = b; + b = a; +} + +function f2(a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) { + a = b; + b = a; +} + +function f3(a: <T>() => { [K in keyof T]: T[K] }, b: <U>() => { [K in keyof U]: U[K] }) { + a = b; + b = a; +} + +// Repro from #18338 + +type IdMapped<T> = { [K in keyof T]: T[K] } + +declare const f: <T>() => IdMapped<T>; +declare const g: <U>() => { [K in keyof U]: U[K] }; + +const h: typeof g = f; + + +//// [higherOrderMappedIndexLookupInference.js] +// @strict +function f1(a, b) { + a = b; + b = a; +} +function f2(a, b) { + a = b; + b = a; +} +function f3(a, b) { + a = b; + b = a; +} +var h = f; diff --git a/tests/baselines/reference/higherOrderMappedIndexLookupInference.symbols b/tests/baselines/reference/higherOrderMappedIndexLookupInference.symbols new file mode 100644 index 0000000000000..44ecb1e6d0219 --- /dev/null +++ b/tests/baselines/reference/higherOrderMappedIndexLookupInference.symbols @@ -0,0 +1,98 @@ +=== tests/cases/compiler/higherOrderMappedIndexLookupInference.ts === +// @strict + +function f1(a: <T>() => keyof T, b: <U>() => keyof U) { +>f1 : Symbol(f1, Decl(higherOrderMappedIndexLookupInference.ts, 0, 0)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 2, 16)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 2, 16)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 2, 37)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 2, 37)) + + a = b; +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32)) + + b = a; +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 2, 32)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 2, 12)) +} + +function f2(a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) { +>f2 : Symbol(f2, Decl(higherOrderMappedIndexLookupInference.ts, 5, 1)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 7, 18)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 7, 16)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 7, 18)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53)) +>L : Symbol(L, Decl(higherOrderMappedIndexLookupInference.ts, 7, 55)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 7, 53)) +>L : Symbol(L, Decl(higherOrderMappedIndexLookupInference.ts, 7, 55)) + + a = b; +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48)) + + b = a; +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 7, 48)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 7, 12)) +} + +function f3(a: <T>() => { [K in keyof T]: T[K] }, b: <U>() => { [K in keyof U]: U[K] }) { +>f3 : Symbol(f3, Decl(higherOrderMappedIndexLookupInference.ts, 10, 1)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 27)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 12, 16)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 27)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 65)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 12, 54)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 12, 65)) + + a = b; +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12)) +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49)) + + b = a; +>b : Symbol(b, Decl(higherOrderMappedIndexLookupInference.ts, 12, 49)) +>a : Symbol(a, Decl(higherOrderMappedIndexLookupInference.ts, 12, 12)) +} + +// Repro from #18338 + +type IdMapped<T> = { [K in keyof T]: T[K] } +>IdMapped : Symbol(IdMapped, Decl(higherOrderMappedIndexLookupInference.ts, 15, 1)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 19, 22)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 19, 14)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 19, 22)) + +declare const f: <T>() => IdMapped<T>; +>f : Symbol(f, Decl(higherOrderMappedIndexLookupInference.ts, 21, 13)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 21, 18)) +>IdMapped : Symbol(IdMapped, Decl(higherOrderMappedIndexLookupInference.ts, 15, 1)) +>T : Symbol(T, Decl(higherOrderMappedIndexLookupInference.ts, 21, 18)) + +declare const g: <U>() => { [K in keyof U]: U[K] }; +>g : Symbol(g, Decl(higherOrderMappedIndexLookupInference.ts, 22, 13)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 22, 29)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18)) +>U : Symbol(U, Decl(higherOrderMappedIndexLookupInference.ts, 22, 18)) +>K : Symbol(K, Decl(higherOrderMappedIndexLookupInference.ts, 22, 29)) + +const h: typeof g = f; +>h : Symbol(h, Decl(higherOrderMappedIndexLookupInference.ts, 24, 5)) +>g : Symbol(g, Decl(higherOrderMappedIndexLookupInference.ts, 22, 13)) +>f : Symbol(f, Decl(higherOrderMappedIndexLookupInference.ts, 21, 13)) + diff --git a/tests/baselines/reference/higherOrderMappedIndexLookupInference.types b/tests/baselines/reference/higherOrderMappedIndexLookupInference.types new file mode 100644 index 0000000000000..f1c2b0aa17ba4 --- /dev/null +++ b/tests/baselines/reference/higherOrderMappedIndexLookupInference.types @@ -0,0 +1,104 @@ +=== tests/cases/compiler/higherOrderMappedIndexLookupInference.ts === +// @strict + +function f1(a: <T>() => keyof T, b: <U>() => keyof U) { +>f1 : (a: <T>() => keyof T, b: <U>() => keyof U) => void +>a : <T>() => keyof T +>T : T +>T : T +>b : <U>() => keyof U +>U : U +>U : U + + a = b; +>a = b : <U>() => keyof U +>a : <T>() => keyof T +>b : <U>() => keyof U + + b = a; +>b = a : <T>() => keyof T +>b : <U>() => keyof U +>a : <T>() => keyof T +} + +function f2(a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) { +>f2 : (a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) => void +>a : <T, K extends keyof T>() => T[K] +>T : T +>K : K +>T : T +>T : T +>K : K +>b : <U, L extends keyof U>() => U[L] +>U : U +>L : L +>U : U +>U : U +>L : L + + a = b; +>a = b : <U, L extends keyof U>() => U[L] +>a : <T, K extends keyof T>() => T[K] +>b : <U, L extends keyof U>() => U[L] + + b = a; +>b = a : <T, K extends keyof T>() => T[K] +>b : <U, L extends keyof U>() => U[L] +>a : <T, K extends keyof T>() => T[K] +} + +function f3(a: <T>() => { [K in keyof T]: T[K] }, b: <U>() => { [K in keyof U]: U[K] }) { +>f3 : (a: <T>() => { [K in keyof T]: T[K]; }, b: <U>() => { [K in keyof U]: U[K]; }) => void +>a : <T>() => { [K in keyof T]: T[K]; } +>T : T +>K : K +>T : T +>T : T +>K : K +>b : <U>() => { [K in keyof U]: U[K]; } +>U : U +>K : K +>U : U +>U : U +>K : K + + a = b; +>a = b : <U>() => { [K in keyof U]: U[K]; } +>a : <T>() => { [K in keyof T]: T[K]; } +>b : <U>() => { [K in keyof U]: U[K]; } + + b = a; +>b = a : <T>() => { [K in keyof T]: T[K]; } +>b : <U>() => { [K in keyof U]: U[K]; } +>a : <T>() => { [K in keyof T]: T[K]; } +} + +// Repro from #18338 + +type IdMapped<T> = { [K in keyof T]: T[K] } +>IdMapped : IdMapped<T> +>T : T +>K : K +>T : T +>T : T +>K : K + +declare const f: <T>() => IdMapped<T>; +>f : <T>() => IdMapped<T> +>T : T +>IdMapped : IdMapped<T> +>T : T + +declare const g: <U>() => { [K in keyof U]: U[K] }; +>g : <U>() => { [K in keyof U]: U[K]; } +>U : U +>K : K +>U : U +>U : U +>K : K + +const h: typeof g = f; +>h : <U>() => { [K in keyof U]: U[K]; } +>g : <U>() => { [K in keyof U]: U[K]; } +>f : <T>() => IdMapped<T> + diff --git a/tests/cases/compiler/higherOrderMappedIndexLookupInference.ts b/tests/cases/compiler/higherOrderMappedIndexLookupInference.ts new file mode 100644 index 0000000000000..9615628391542 --- /dev/null +++ b/tests/cases/compiler/higherOrderMappedIndexLookupInference.ts @@ -0,0 +1,25 @@ +// @strict + +function f1(a: <T>() => keyof T, b: <U>() => keyof U) { + a = b; + b = a; +} + +function f2(a: <T, K extends keyof T>() => T[K], b: <U, L extends keyof U>() => U[L]) { + a = b; + b = a; +} + +function f3(a: <T>() => { [K in keyof T]: T[K] }, b: <U>() => { [K in keyof U]: U[K] }) { + a = b; + b = a; +} + +// Repro from #18338 + +type IdMapped<T> = { [K in keyof T]: T[K] } + +declare const f: <T>() => IdMapped<T>; +declare const g: <U>() => { [K in keyof U]: U[K] }; + +const h: typeof g = f;