Treat array literal contextually typed by homomorphic mapped types as in tuple context (#56555)

This commit is contained in:
Mateusz Burzyński 2023-12-14 00:12:38 +01:00 committed by GitHub
parent b527b9059a
commit 2c7162143b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 328 additions and 2 deletions

View File

@ -31053,7 +31053,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];

View File

@ -0,0 +1,148 @@
//// [tests/cases/compiler/reverseMappedTupleContext.ts] ////
=== reverseMappedTupleContext.ts ===
// https://github.com/microsoft/TypeScript/issues/55382
declare function test1<T>(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<T extends readonly unknown[]>(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<string, unknown> | readonly unknown[];
>Schema : Symbol(Schema, Decl(reverseMappedTupleContext.ts, 10, 35))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
type Definition<T> = {
>Definition : Symbol(Definition, Decl(reverseMappedTupleContext.ts, 12, 59))
>T : Symbol(T, Decl(reverseMappedTupleContext.ts, 13, 16))
[K in keyof T]: (() => T[K]) | Definition<T[K]>;
>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<T extends Schema>(definition: Definition<T>): 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))
});
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<T extends string[]> = {
>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<T extends Record<string, string[]>>(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<T[K]>;
>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))
},
});

View File

@ -0,0 +1,130 @@
//// [tests/cases/compiler/reverseMappedTupleContext.ts] ////
=== reverseMappedTupleContext.ts ===
// https://github.com/microsoft/TypeScript/issues/55382
declare function test1<T>(arg: {
>test1 : <T>(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 : <T>(arg: { [K in keyof T]: T[K]; }) => T
>["foo", 42] : [string, number]
>"foo" : "foo"
>42 : 42
declare function test2<T extends readonly unknown[]>(arg: {
>test2 : <T extends readonly unknown[]>(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 : <T extends readonly unknown[]>(arg: { [K in keyof T]: T[K]; }) => T
>["foo", 42] : [string, number]
>"foo" : "foo"
>42 : 42
type Schema = Record<string, unknown> | readonly unknown[];
>Schema : readonly unknown[] | Record<string, unknown>
type Definition<T> = {
>Definition : Definition<T>
[K in keyof T]: (() => T[K]) | Definition<T[K]>;
};
declare function create<T extends Schema>(definition: Definition<T>): T;
>create : <T extends Schema>(definition: Definition<T>) => T
>definition : Definition<T>
const created1 = create([() => 1, [() => ""]]);
>created1 : [number, [string]]
>create([() => 1, [() => ""]]) : [number, [string]]
>create : <T extends Schema>(definition: Definition<T>) => 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 : <T extends Schema>(definition: Definition<T>) => T
>{ a: () => 1, b: [() => ""],} : { a: () => number; b: [() => string]; }
a: () => 1,
>a : () => number
>() => 1 : () => number
>1 : 1
b: [() => ""],
>b : [() => string]
>[() => ""] : [() => string]
>() => "" : () => string
>"" : ""
});
interface CompilerOptions {
allowUnreachableCode?: boolean;
>allowUnreachableCode : boolean | undefined
allowUnusedLabels?: boolean;
>allowUnusedLabels : boolean | undefined
alwaysStrict?: boolean;
>alwaysStrict : boolean | undefined
}
type KeepLiteralStrings<T extends string[]> = {
>KeepLiteralStrings : KeepLiteralStrings<T>
[K in keyof T]: T[K];
};
declare function test4<T extends Record<string, string[]>>(obj: {
>test4 : <T extends Record<string, string[]>>(obj: { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings<T[K]>; }; }) => T
>obj : { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings<T[K]>; }; }
[K in keyof T & keyof CompilerOptions]: {
dependencies: KeepLiteralStrings<T[K]>;
>dependencies : KeepLiteralStrings<T[K]>
};
}): 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 : <T extends Record<string, string[]>>(obj: { [K in keyof T & keyof CompilerOptions]: { dependencies: KeepLiteralStrings<T[K]>; }; }) => 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"
},
});

View File

@ -142,7 +142,7 @@ const myUnion = unionType([identifierExtractor, stringExtractor]);
>myUnion : AnyExtractor<{ node: Identifier; kind: "identifier"; value: string; } | { node: StringLiteral; kind: "string"; value: string; }>
>unionType([identifierExtractor, stringExtractor]) : AnyExtractor<{ node: Identifier; kind: "identifier"; value: string; } | { node: StringLiteral; kind: "string"; value: string; }>
>unionType : <Result extends readonly unknown[]>(parsers: { [K in keyof Result]: AnyExtractor<Result[K]>; }) => AnyExtractor<Result[number]>
>[identifierExtractor, stringExtractor] : (Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }> | Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>)[]
>[identifierExtractor, stringExtractor] : [Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }>, Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>]
>identifierExtractor : Extractor<Identifier, { node: Identifier; kind: "identifier"; value: string; }>
>stringExtractor : Extractor<StringLiteral, { node: StringLiteral; kind: "string"; value: string; }>

View File

@ -0,0 +1,47 @@
// @strict: true
// @noEmit: true
// https://github.com/microsoft/TypeScript/issues/55382
declare function test1<T>(arg: {
[K in keyof T]: T[K];
}): T;
const result1 = test1(["foo", 42]);
declare function test2<T extends readonly unknown[]>(arg: {
[K in keyof T]: T[K];
}): T;
const result2 = test2(["foo", 42]);
type Schema = Record<string, unknown> | readonly unknown[];
type Definition<T> = {
[K in keyof T]: (() => T[K]) | Definition<T[K]>;
};
declare function create<T extends Schema>(definition: Definition<T>): T;
const created1 = create([() => 1, [() => ""]]);
const created2 = create({
a: () => 1,
b: [() => ""],
});
interface CompilerOptions {
allowUnreachableCode?: boolean;
allowUnusedLabels?: boolean;
alwaysStrict?: boolean;
}
type KeepLiteralStrings<T extends string[]> = {
[K in keyof T]: T[K];
};
declare function test4<T extends Record<string, string[]>>(obj: {
[K in keyof T & keyof CompilerOptions]: {
dependencies: KeepLiteralStrings<T[K]>;
};
}): T;
const result4 = test4({
alwaysStrict: {
dependencies: ["foo", "bar"],
},
allowUnusedLabels: {
dependencies: ["baz", "qwe"],
},
});