From 7deb72c5ed03deb3a9d356496e40a22c0f68b7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 27 Nov 2023 09:00:19 +0100 Subject: [PATCH 1/2] Treat array literal contextually typed by homomorphic mapped types as in tuple context --- src/compiler/checker.ts | 3 +- .../reverseMappedTupleContext.symbols | 84 +++++++++++++++++++ .../reference/reverseMappedTupleContext.types | 75 +++++++++++++++++ .../compiler/reverseMappedTupleContext.ts | 25 ++++++ 4 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/reverseMappedTupleContext.symbols create mode 100644 tests/baselines/reference/reverseMappedTupleContext.types create mode 100644 tests/cases/compiler/reverseMappedTupleContext.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 985b0f4968670..530bcb92b30a8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30877,7 +30877,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const inDestructuringPattern = isAssignmentTarget(node); const inConstContext = isConstContext(node); const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined); - const inTupleContext = isSpreadIntoCallOrNew(node) || !!contextualType && someType(contextualType, isTupleLikeType); + const inTupleContext = isSpreadIntoCallOrNew(node) || !!contextualType && someType(contextualType, t => isTupleLikeType(t) || isGenericMappedType(t) && !t.nameType && !!getHomomorphicTypeVariable(t.target as MappedType || t)); + let hasOmittedExpression = false; for (let i = 0; i < elementCount; i++) { const e = elements[i]; diff --git a/tests/baselines/reference/reverseMappedTupleContext.symbols b/tests/baselines/reference/reverseMappedTupleContext.symbols new file mode 100644 index 0000000000000..dbd9e0b500862 --- /dev/null +++ b/tests/baselines/reference/reverseMappedTupleContext.symbols @@ -0,0 +1,84 @@ +//// [tests/cases/compiler/reverseMappedTupleContext.ts] //// + +=== reverseMappedTupleContext.ts === +// https://github.com/microsoft/TypeScript/issues/55382 + +declare function test1(arg: { +>test1 : Symbol(test1, Decl(reverseMappedTupleContext.ts, 0, 0)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 2, 23)) +>arg : Symbol(arg, Decl(reverseMappedTupleContext.ts, 2, 26)) + + [K in keyof T]: T[K]; +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 3, 3)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 2, 23)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 2, 23)) +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 3, 3)) + +}): T; +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 2, 23)) + +const result1 = test1(["foo", 42]); +>result1 : Symbol(result1, Decl(reverseMappedTupleContext.ts, 5, 5)) +>test1 : Symbol(test1, Decl(reverseMappedTupleContext.ts, 0, 0)) + +declare function test2(arg: { +>test2 : Symbol(test2, Decl(reverseMappedTupleContext.ts, 5, 35)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 7, 23)) +>arg : Symbol(arg, Decl(reverseMappedTupleContext.ts, 7, 53)) + + [K in keyof T]: T[K]; +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 8, 3)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 7, 23)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 7, 23)) +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 8, 3)) + +}): T; +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 7, 23)) + +const result2 = test2(["foo", 42]); +>result2 : Symbol(result2, Decl(reverseMappedTupleContext.ts, 10, 5)) +>test2 : Symbol(test2, Decl(reverseMappedTupleContext.ts, 5, 35)) + +type Schema = Record | readonly unknown[]; +>Schema : Symbol(Schema, Decl(reverseMappedTupleContext.ts, 10, 35)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +type Definition = { +>Definition : Symbol(Definition, Decl(reverseMappedTupleContext.ts, 12, 59)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16)) + + [K in keyof T]: (() => T[K]) | Definition; +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 14, 3)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16)) +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 14, 3)) +>Definition : Symbol(Definition, Decl(reverseMappedTupleContext.ts, 12, 59)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16)) +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 14, 3)) + +}; +declare function create(definition: Definition): T; +>create : Symbol(create, Decl(reverseMappedTupleContext.ts, 15, 2)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 16, 24)) +>Schema : Symbol(Schema, Decl(reverseMappedTupleContext.ts, 10, 35)) +>definition : Symbol(definition, Decl(reverseMappedTupleContext.ts, 16, 42)) +>Definition : Symbol(Definition, Decl(reverseMappedTupleContext.ts, 12, 59)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 16, 24)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 16, 24)) + +const created1 = create([() => 1, [() => ""]]); +>created1 : Symbol(created1, Decl(reverseMappedTupleContext.ts, 17, 5)) +>create : Symbol(create, Decl(reverseMappedTupleContext.ts, 15, 2)) + +const created2 = create({ +>created2 : Symbol(created2, Decl(reverseMappedTupleContext.ts, 18, 5)) +>create : Symbol(create, Decl(reverseMappedTupleContext.ts, 15, 2)) + + a: () => 1, +>a : Symbol(a, Decl(reverseMappedTupleContext.ts, 18, 25)) + + b: [() => ""], +>b : Symbol(b, Decl(reverseMappedTupleContext.ts, 19, 13)) + +}); + diff --git a/tests/baselines/reference/reverseMappedTupleContext.types b/tests/baselines/reference/reverseMappedTupleContext.types new file mode 100644 index 0000000000000..8b350eb4305b1 --- /dev/null +++ b/tests/baselines/reference/reverseMappedTupleContext.types @@ -0,0 +1,75 @@ +//// [tests/cases/compiler/reverseMappedTupleContext.ts] //// + +=== reverseMappedTupleContext.ts === +// https://github.com/microsoft/TypeScript/issues/55382 + +declare function test1(arg: { +>test1 : (arg: { [K in keyof T]: T[K]; }) => T +>arg : { [K in keyof T]: T[K]; } + + [K in keyof T]: T[K]; +}): T; +const result1 = test1(["foo", 42]); +>result1 : [string, number] +>test1(["foo", 42]) : [string, number] +>test1 : (arg: { [K in keyof T]: T[K]; }) => T +>["foo", 42] : [string, number] +>"foo" : "foo" +>42 : 42 + +declare function test2(arg: { +>test2 : (arg: { [K in keyof T]: T[K]; }) => T +>arg : { [K in keyof T]: T[K]; } + + [K in keyof T]: T[K]; +}): T; +const result2 = test2(["foo", 42]); +>result2 : [string, number] +>test2(["foo", 42]) : [string, number] +>test2 : (arg: { [K in keyof T]: T[K]; }) => T +>["foo", 42] : [string, number] +>"foo" : "foo" +>42 : 42 + +type Schema = Record | readonly unknown[]; +>Schema : readonly unknown[] | Record + +type Definition = { +>Definition : Definition + + [K in keyof T]: (() => T[K]) | Definition; +}; +declare function create(definition: Definition): T; +>create : (definition: Definition) => T +>definition : Definition + +const created1 = create([() => 1, [() => ""]]); +>created1 : [number, [string]] +>create([() => 1, [() => ""]]) : [number, [string]] +>create : (definition: Definition) => T +>[() => 1, [() => ""]] : [() => number, [() => string]] +>() => 1 : () => number +>1 : 1 +>[() => ""] : [() => string] +>() => "" : () => string +>"" : "" + +const created2 = create({ +>created2 : { a: number; b: [string]; } +>create({ a: () => 1, b: [() => ""],}) : { a: number; b: [string]; } +>create : (definition: Definition) => T +>{ a: () => 1, b: [() => ""],} : { a: () => number; b: [() => string]; } + + a: () => 1, +>a : () => number +>() => 1 : () => number +>1 : 1 + + b: [() => ""], +>b : [() => string] +>[() => ""] : [() => string] +>() => "" : () => string +>"" : "" + +}); + diff --git a/tests/cases/compiler/reverseMappedTupleContext.ts b/tests/cases/compiler/reverseMappedTupleContext.ts new file mode 100644 index 0000000000000..f94c4e73a06a6 --- /dev/null +++ b/tests/cases/compiler/reverseMappedTupleContext.ts @@ -0,0 +1,25 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/55382 + +declare function test1(arg: { + [K in keyof T]: T[K]; +}): T; +const result1 = test1(["foo", 42]); + +declare function test2(arg: { + [K in keyof T]: T[K]; +}): T; +const result2 = test2(["foo", 42]); + +type Schema = Record | readonly unknown[]; +type Definition = { + [K in keyof T]: (() => T[K]) | Definition; +}; +declare function create(definition: Definition): T; +const created1 = create([() => 1, [() => ""]]); +const created2 = create({ + a: () => 1, + b: [() => ""], +}); From ba2f4657d1f56c4006d84c2abedfdd815dd299f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 8 Dec 2023 12:11:54 +0100 Subject: [PATCH 2/2] add an extra test case --- .../reverseMappedTupleContext.symbols | 64 +++++++++++++++++++ .../reference/reverseMappedTupleContext.types | 55 ++++++++++++++++ .../compiler/reverseMappedTupleContext.ts | 22 +++++++ 3 files changed, 141 insertions(+) diff --git a/tests/baselines/reference/reverseMappedTupleContext.symbols b/tests/baselines/reference/reverseMappedTupleContext.symbols index dbd9e0b500862..0c3e8384a3392 100644 --- a/tests/baselines/reference/reverseMappedTupleContext.symbols +++ b/tests/baselines/reference/reverseMappedTupleContext.symbols @@ -82,3 +82,67 @@ const created2 = create({ }); +interface CompilerOptions { +>CompilerOptions : Symbol(CompilerOptions, Decl(reverseMappedTupleContext.ts, 21, 3)) + + allowUnreachableCode?: boolean; +>allowUnreachableCode : Symbol(CompilerOptions.allowUnreachableCode, Decl(reverseMappedTupleContext.ts, 23, 27)) + + allowUnusedLabels?: boolean; +>allowUnusedLabels : Symbol(CompilerOptions.allowUnusedLabels, Decl(reverseMappedTupleContext.ts, 24, 33)) + + alwaysStrict?: boolean; +>alwaysStrict : Symbol(CompilerOptions.alwaysStrict, Decl(reverseMappedTupleContext.ts, 25, 30)) +} +type KeepLiteralStrings = { +>KeepLiteralStrings : Symbol(KeepLiteralStrings, Decl(reverseMappedTupleContext.ts, 27, 1)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 28, 24)) + + [K in keyof T]: T[K]; +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 29, 3)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 28, 24)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 28, 24)) +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 29, 3)) + +}; +declare function test4>(obj: { +>test4 : Symbol(test4, Decl(reverseMappedTupleContext.ts, 30, 2)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 31, 23)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>obj : Symbol(obj, Decl(reverseMappedTupleContext.ts, 31, 59)) + + [K in keyof T & keyof CompilerOptions]: { +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 32, 3)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 31, 23)) +>CompilerOptions : Symbol(CompilerOptions, Decl(reverseMappedTupleContext.ts, 21, 3)) + + dependencies: KeepLiteralStrings; +>dependencies : Symbol(dependencies, Decl(reverseMappedTupleContext.ts, 32, 43)) +>KeepLiteralStrings : Symbol(KeepLiteralStrings, Decl(reverseMappedTupleContext.ts, 27, 1)) +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 31, 23)) +>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 32, 3)) + + }; +}): T; +>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 31, 23)) + +const result4 = test4({ +>result4 : Symbol(result4, Decl(reverseMappedTupleContext.ts, 36, 5)) +>test4 : Symbol(test4, Decl(reverseMappedTupleContext.ts, 30, 2)) + + alwaysStrict: { +>alwaysStrict : Symbol(alwaysStrict, Decl(reverseMappedTupleContext.ts, 36, 23)) + + dependencies: ["foo", "bar"], +>dependencies : Symbol(dependencies, Decl(reverseMappedTupleContext.ts, 37, 17)) + + }, + allowUnusedLabels: { +>allowUnusedLabels : Symbol(allowUnusedLabels, Decl(reverseMappedTupleContext.ts, 39, 4)) + + dependencies: ["baz", "qwe"], +>dependencies : Symbol(dependencies, Decl(reverseMappedTupleContext.ts, 40, 22)) + + }, +}); + diff --git a/tests/baselines/reference/reverseMappedTupleContext.types b/tests/baselines/reference/reverseMappedTupleContext.types index 8b350eb4305b1..5e65b21b770e4 100644 --- a/tests/baselines/reference/reverseMappedTupleContext.types +++ b/tests/baselines/reference/reverseMappedTupleContext.types @@ -73,3 +73,58 @@ const created2 = create({ }); +interface CompilerOptions { + allowUnreachableCode?: boolean; +>allowUnreachableCode : boolean | undefined + + allowUnusedLabels?: boolean; +>allowUnusedLabels : boolean | undefined + + alwaysStrict?: boolean; +>alwaysStrict : boolean | undefined +} +type KeepLiteralStrings = { +>KeepLiteralStrings : KeepLiteralStrings + + [K in keyof T]: T[K]; +}; +declare function test4>(obj: { +>test4 : >(obj: { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings; }; }) => T +>obj : { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings; }; } + + [K in keyof T & keyof CompilerOptions]: { + dependencies: KeepLiteralStrings; +>dependencies : KeepLiteralStrings + + }; +}): T; +const result4 = test4({ +>result4 : { alwaysStrict: ["foo", "bar"]; allowUnusedLabels: ["baz", "qwe"]; } +>test4({ alwaysStrict: { dependencies: ["foo", "bar"], }, allowUnusedLabels: { dependencies: ["baz", "qwe"], },}) : { alwaysStrict: ["foo", "bar"]; allowUnusedLabels: ["baz", "qwe"]; } +>test4 : >(obj: { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings; }; }) => T +>{ alwaysStrict: { dependencies: ["foo", "bar"], }, allowUnusedLabels: { dependencies: ["baz", "qwe"], },} : { alwaysStrict: { dependencies: ["foo", "bar"]; }; allowUnusedLabels: { dependencies: ["baz", "qwe"]; }; } + + alwaysStrict: { +>alwaysStrict : { dependencies: ["foo", "bar"]; } +>{ dependencies: ["foo", "bar"], } : { dependencies: ["foo", "bar"]; } + + dependencies: ["foo", "bar"], +>dependencies : ["foo", "bar"] +>["foo", "bar"] : ["foo", "bar"] +>"foo" : "foo" +>"bar" : "bar" + + }, + allowUnusedLabels: { +>allowUnusedLabels : { dependencies: ["baz", "qwe"]; } +>{ dependencies: ["baz", "qwe"], } : { dependencies: ["baz", "qwe"]; } + + dependencies: ["baz", "qwe"], +>dependencies : ["baz", "qwe"] +>["baz", "qwe"] : ["baz", "qwe"] +>"baz" : "baz" +>"qwe" : "qwe" + + }, +}); + diff --git a/tests/cases/compiler/reverseMappedTupleContext.ts b/tests/cases/compiler/reverseMappedTupleContext.ts index f94c4e73a06a6..a2d2bcfe508d0 100644 --- a/tests/cases/compiler/reverseMappedTupleContext.ts +++ b/tests/cases/compiler/reverseMappedTupleContext.ts @@ -23,3 +23,25 @@ const created2 = create({ a: () => 1, b: [() => ""], }); + +interface CompilerOptions { + allowUnreachableCode?: boolean; + allowUnusedLabels?: boolean; + alwaysStrict?: boolean; +} +type KeepLiteralStrings = { + [K in keyof T]: T[K]; +}; +declare function test4>(obj: { + [K in keyof T & keyof CompilerOptions]: { + dependencies: KeepLiteralStrings; + }; +}): T; +const result4 = test4({ + alwaysStrict: { + dependencies: ["foo", "bar"], + }, + allowUnusedLabels: { + dependencies: ["baz", "qwe"], + }, +});