Skip to content

Commit b1db40e

Browse files
Andaristc0sta
authored andcommitted
Treat array literal contextually typed by homomorphic mapped types as in tuple context (microsoft#56555)
1 parent 895b5c9 commit b1db40e

File tree

5 files changed

+328
-2
lines changed

5 files changed

+328
-2
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31071,7 +31071,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3107131071
const inDestructuringPattern = isAssignmentTarget(node);
3107231072
const inConstContext = isConstContext(node);
3107331073
const contextualType = getApparentTypeOfContextualType(node, /*contextFlags*/ undefined);
31074-
const inTupleContext = isSpreadIntoCallOrNew(node) || !!contextualType && someType(contextualType, isTupleLikeType);
31074+
const inTupleContext = isSpreadIntoCallOrNew(node) || !!contextualType && someType(contextualType, t => isTupleLikeType(t) || isGenericMappedType(t) && !t.nameType && !!getHomomorphicTypeVariable(t.target as MappedType || t));
31075+
3107531076
let hasOmittedExpression = false;
3107631077
for (let i = 0; i < elementCount; i++) {
3107731078
const e = elements[i];
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//// [tests/cases/compiler/reverseMappedTupleContext.ts] ////
2+
3+
=== reverseMappedTupleContext.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/55382
5+
6+
declare function test1<T>(arg: {
7+
>test1 : Symbol(test1, Decl(reverseMappedTupleContext.ts, 0, 0))
8+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 2, 23))
9+
>arg : Symbol(arg, Decl(reverseMappedTupleContext.ts, 2, 26))
10+
11+
[K in keyof T]: T[K];
12+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 3, 3))
13+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 2, 23))
14+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 2, 23))
15+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 3, 3))
16+
17+
}): T;
18+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 2, 23))
19+
20+
const result1 = test1(["foo", 42]);
21+
>result1 : Symbol(result1, Decl(reverseMappedTupleContext.ts, 5, 5))
22+
>test1 : Symbol(test1, Decl(reverseMappedTupleContext.ts, 0, 0))
23+
24+
declare function test2<T extends readonly unknown[]>(arg: {
25+
>test2 : Symbol(test2, Decl(reverseMappedTupleContext.ts, 5, 35))
26+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 7, 23))
27+
>arg : Symbol(arg, Decl(reverseMappedTupleContext.ts, 7, 53))
28+
29+
[K in keyof T]: T[K];
30+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 8, 3))
31+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 7, 23))
32+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 7, 23))
33+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 8, 3))
34+
35+
}): T;
36+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 7, 23))
37+
38+
const result2 = test2(["foo", 42]);
39+
>result2 : Symbol(result2, Decl(reverseMappedTupleContext.ts, 10, 5))
40+
>test2 : Symbol(test2, Decl(reverseMappedTupleContext.ts, 5, 35))
41+
42+
type Schema = Record<string, unknown> | readonly unknown[];
43+
>Schema : Symbol(Schema, Decl(reverseMappedTupleContext.ts, 10, 35))
44+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
45+
46+
type Definition<T> = {
47+
>Definition : Symbol(Definition, Decl(reverseMappedTupleContext.ts, 12, 59))
48+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16))
49+
50+
[K in keyof T]: (() => T[K]) | Definition<T[K]>;
51+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 14, 3))
52+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16))
53+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16))
54+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 14, 3))
55+
>Definition : Symbol(Definition, Decl(reverseMappedTupleContext.ts, 12, 59))
56+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16))
57+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 14, 3))
58+
59+
};
60+
declare function create<T extends Schema>(definition: Definition<T>): T;
61+
>create : Symbol(create, Decl(reverseMappedTupleContext.ts, 15, 2))
62+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 16, 24))
63+
>Schema : Symbol(Schema, Decl(reverseMappedTupleContext.ts, 10, 35))
64+
>definition : Symbol(definition, Decl(reverseMappedTupleContext.ts, 16, 42))
65+
>Definition : Symbol(Definition, Decl(reverseMappedTupleContext.ts, 12, 59))
66+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 16, 24))
67+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 16, 24))
68+
69+
const created1 = create([() => 1, [() => ""]]);
70+
>created1 : Symbol(created1, Decl(reverseMappedTupleContext.ts, 17, 5))
71+
>create : Symbol(create, Decl(reverseMappedTupleContext.ts, 15, 2))
72+
73+
const created2 = create({
74+
>created2 : Symbol(created2, Decl(reverseMappedTupleContext.ts, 18, 5))
75+
>create : Symbol(create, Decl(reverseMappedTupleContext.ts, 15, 2))
76+
77+
a: () => 1,
78+
>a : Symbol(a, Decl(reverseMappedTupleContext.ts, 18, 25))
79+
80+
b: [() => ""],
81+
>b : Symbol(b, Decl(reverseMappedTupleContext.ts, 19, 13))
82+
83+
});
84+
85+
interface CompilerOptions {
86+
>CompilerOptions : Symbol(CompilerOptions, Decl(reverseMappedTupleContext.ts, 21, 3))
87+
88+
allowUnreachableCode?: boolean;
89+
>allowUnreachableCode : Symbol(CompilerOptions.allowUnreachableCode, Decl(reverseMappedTupleContext.ts, 23, 27))
90+
91+
allowUnusedLabels?: boolean;
92+
>allowUnusedLabels : Symbol(CompilerOptions.allowUnusedLabels, Decl(reverseMappedTupleContext.ts, 24, 33))
93+
94+
alwaysStrict?: boolean;
95+
>alwaysStrict : Symbol(CompilerOptions.alwaysStrict, Decl(reverseMappedTupleContext.ts, 25, 30))
96+
}
97+
type KeepLiteralStrings<T extends string[]> = {
98+
>KeepLiteralStrings : Symbol(KeepLiteralStrings, Decl(reverseMappedTupleContext.ts, 27, 1))
99+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 28, 24))
100+
101+
[K in keyof T]: T[K];
102+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 29, 3))
103+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 28, 24))
104+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 28, 24))
105+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 29, 3))
106+
107+
};
108+
declare function test4<T extends Record<string, string[]>>(obj: {
109+
>test4 : Symbol(test4, Decl(reverseMappedTupleContext.ts, 30, 2))
110+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 31, 23))
111+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
112+
>obj : Symbol(obj, Decl(reverseMappedTupleContext.ts, 31, 59))
113+
114+
[K in keyof T & keyof CompilerOptions]: {
115+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 32, 3))
116+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 31, 23))
117+
>CompilerOptions : Symbol(CompilerOptions, Decl(reverseMappedTupleContext.ts, 21, 3))
118+
119+
dependencies: KeepLiteralStrings<T[K]>;
120+
>dependencies : Symbol(dependencies, Decl(reverseMappedTupleContext.ts, 32, 43))
121+
>KeepLiteralStrings : Symbol(KeepLiteralStrings, Decl(reverseMappedTupleContext.ts, 27, 1))
122+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 31, 23))
123+
>K : Symbol(K, Decl(reverseMappedTupleContext.ts, 32, 3))
124+
125+
};
126+
}): T;
127+
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 31, 23))
128+
129+
const result4 = test4({
130+
>result4 : Symbol(result4, Decl(reverseMappedTupleContext.ts, 36, 5))
131+
>test4 : Symbol(test4, Decl(reverseMappedTupleContext.ts, 30, 2))
132+
133+
alwaysStrict: {
134+
>alwaysStrict : Symbol(alwaysStrict, Decl(reverseMappedTupleContext.ts, 36, 23))
135+
136+
dependencies: ["foo", "bar"],
137+
>dependencies : Symbol(dependencies, Decl(reverseMappedTupleContext.ts, 37, 17))
138+
139+
},
140+
allowUnusedLabels: {
141+
>allowUnusedLabels : Symbol(allowUnusedLabels, Decl(reverseMappedTupleContext.ts, 39, 4))
142+
143+
dependencies: ["baz", "qwe"],
144+
>dependencies : Symbol(dependencies, Decl(reverseMappedTupleContext.ts, 40, 22))
145+
146+
},
147+
});
148+
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
//// [tests/cases/compiler/reverseMappedTupleContext.ts] ////
2+
3+
=== reverseMappedTupleContext.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/55382
5+
6+
declare function test1<T>(arg: {
7+
>test1 : <T>(arg: { [K in keyof T]: T[K]; }) => T
8+
>arg : { [K in keyof T]: T[K]; }
9+
10+
[K in keyof T]: T[K];
11+
}): T;
12+
const result1 = test1(["foo", 42]);
13+
>result1 : [string, number]
14+
>test1(["foo", 42]) : [string, number]
15+
>test1 : <T>(arg: { [K in keyof T]: T[K]; }) => T
16+
>["foo", 42] : [string, number]
17+
>"foo" : "foo"
18+
>42 : 42
19+
20+
declare function test2<T extends readonly unknown[]>(arg: {
21+
>test2 : <T extends readonly unknown[]>(arg: { [K in keyof T]: T[K]; }) => T
22+
>arg : { [K in keyof T]: T[K]; }
23+
24+
[K in keyof T]: T[K];
25+
}): T;
26+
const result2 = test2(["foo", 42]);
27+
>result2 : [string, number]
28+
>test2(["foo", 42]) : [string, number]
29+
>test2 : <T extends readonly unknown[]>(arg: { [K in keyof T]: T[K]; }) => T
30+
>["foo", 42] : [string, number]
31+
>"foo" : "foo"
32+
>42 : 42
33+
34+
type Schema = Record<string, unknown> | readonly unknown[];
35+
>Schema : readonly unknown[] | Record<string, unknown>
36+
37+
type Definition<T> = {
38+
>Definition : Definition<T>
39+
40+
[K in keyof T]: (() => T[K]) | Definition<T[K]>;
41+
};
42+
declare function create<T extends Schema>(definition: Definition<T>): T;
43+
>create : <T extends Schema>(definition: Definition<T>) => T
44+
>definition : Definition<T>
45+
46+
const created1 = create([() => 1, [() => ""]]);
47+
>created1 : [number, [string]]
48+
>create([() => 1, [() => ""]]) : [number, [string]]
49+
>create : <T extends Schema>(definition: Definition<T>) => T
50+
>[() => 1, [() => ""]] : [() => number, [() => string]]
51+
>() => 1 : () => number
52+
>1 : 1
53+
>[() => ""] : [() => string]
54+
>() => "" : () => string
55+
>"" : ""
56+
57+
const created2 = create({
58+
>created2 : { a: number; b: [string]; }
59+
>create({ a: () => 1, b: [() => ""],}) : { a: number; b: [string]; }
60+
>create : <T extends Schema>(definition: Definition<T>) => T
61+
>{ a: () => 1, b: [() => ""],} : { a: () => number; b: [() => string]; }
62+
63+
a: () => 1,
64+
>a : () => number
65+
>() => 1 : () => number
66+
>1 : 1
67+
68+
b: [() => ""],
69+
>b : [() => string]
70+
>[() => ""] : [() => string]
71+
>() => "" : () => string
72+
>"" : ""
73+
74+
});
75+
76+
interface CompilerOptions {
77+
allowUnreachableCode?: boolean;
78+
>allowUnreachableCode : boolean | undefined
79+
80+
allowUnusedLabels?: boolean;
81+
>allowUnusedLabels : boolean | undefined
82+
83+
alwaysStrict?: boolean;
84+
>alwaysStrict : boolean | undefined
85+
}
86+
type KeepLiteralStrings<T extends string[]> = {
87+
>KeepLiteralStrings : KeepLiteralStrings<T>
88+
89+
[K in keyof T]: T[K];
90+
};
91+
declare function test4<T extends Record<string, string[]>>(obj: {
92+
>test4 : <T extends Record<string, string[]>>(obj: { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings<T[K]>; }; }) => T
93+
>obj : { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings<T[K]>; }; }
94+
95+
[K in keyof T & keyof CompilerOptions]: {
96+
dependencies: KeepLiteralStrings<T[K]>;
97+
>dependencies : KeepLiteralStrings<T[K]>
98+
99+
};
100+
}): T;
101+
const result4 = test4({
102+
>result4 : { alwaysStrict: ["foo", "bar"]; allowUnusedLabels: ["baz", "qwe"]; }
103+
>test4({ alwaysStrict: { dependencies: ["foo", "bar"], }, allowUnusedLabels: { dependencies: ["baz", "qwe"], },}) : { alwaysStrict: ["foo", "bar"]; allowUnusedLabels: ["baz", "qwe"]; }
104+
>test4 : <T extends Record<string, string[]>>(obj: { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings<T[K]>; }; }) => T
105+
>{ alwaysStrict: { dependencies: ["foo", "bar"], }, allowUnusedLabels: { dependencies: ["baz", "qwe"], },} : { alwaysStrict: { dependencies: ["foo", "bar"]; }; allowUnusedLabels: { dependencies: ["baz", "qwe"]; }; }
106+
107+
alwaysStrict: {
108+
>alwaysStrict : { dependencies: ["foo", "bar"]; }
109+
>{ dependencies: ["foo", "bar"], } : { dependencies: ["foo", "bar"]; }
110+
111+
dependencies: ["foo", "bar"],
112+
>dependencies : ["foo", "bar"]
113+
>["foo", "bar"] : ["foo", "bar"]
114+
>"foo" : "foo"
115+
>"bar" : "bar"
116+
117+
},
118+
allowUnusedLabels: {
119+
>allowUnusedLabels : { dependencies: ["baz", "qwe"]; }
120+
>{ dependencies: ["baz", "qwe"], } : { dependencies: ["baz", "qwe"]; }
121+
122+
dependencies: ["baz", "qwe"],
123+
>dependencies : ["baz", "qwe"]
124+
>["baz", "qwe"] : ["baz", "qwe"]
125+
>"baz" : "baz"
126+
>"qwe" : "qwe"
127+
128+
},
129+
});
130+

tests/baselines/reference/reverseMappedUnionInference.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ const myUnion = unionType([identifierExtractor, stringExtractor]);
142142
>myUnion : AnyExtractor<{ node: Identifier; kind: "identifier"; value: string; } | { node: StringLiteral; kind: "string"; value: string; }>
143143
>unionType([identifierExtractor, stringExtractor]) : AnyExtractor<{ node: Identifier; kind: "identifier"; value: string; } | { node: StringLiteral; kind: "string"; value: string; }>
144144
>unionType : <Result extends readonly unknown[]>(parsers: { [K in keyof Result]: AnyExtractor<Result[K]>; }) => AnyExtractor<Result[number]>
145-
>[identifierExtractor, stringExtractor] : (Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }> | Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>)[]
145+
>[identifierExtractor, stringExtractor] : [Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }>, Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>]
146146
>identifierExtractor : Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }>
147147
>stringExtractor : Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>
148148

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/55382
5+
6+
declare function test1<T>(arg: {
7+
[K in keyof T]: T[K];
8+
}): T;
9+
const result1 = test1(["foo", 42]);
10+
11+
declare function test2<T extends readonly unknown[]>(arg: {
12+
[K in keyof T]: T[K];
13+
}): T;
14+
const result2 = test2(["foo", 42]);
15+
16+
type Schema = Record<string, unknown> | readonly unknown[];
17+
type Definition<T> = {
18+
[K in keyof T]: (() => T[K]) | Definition<T[K]>;
19+
};
20+
declare function create<T extends Schema>(definition: Definition<T>): T;
21+
const created1 = create([() => 1, [() => ""]]);
22+
const created2 = create({
23+
a: () => 1,
24+
b: [() => ""],
25+
});
26+
27+
interface CompilerOptions {
28+
allowUnreachableCode?: boolean;
29+
allowUnusedLabels?: boolean;
30+
alwaysStrict?: boolean;
31+
}
32+
type KeepLiteralStrings<T extends string[]> = {
33+
[K in keyof T]: T[K];
34+
};
35+
declare function test4<T extends Record<string, string[]>>(obj: {
36+
[K in keyof T & keyof CompilerOptions]: {
37+
dependencies: KeepLiteralStrings<T[K]>;
38+
};
39+
}): T;
40+
const result4 = test4({
41+
alwaysStrict: {
42+
dependencies: ["foo", "bar"],
43+
},
44+
allowUnusedLabels: {
45+
dependencies: ["baz", "qwe"],
46+
},
47+
});

0 commit comments

Comments
 (0)