From 7150d6803825449cbf6935267666f288b056594f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 1 Mar 2023 12:47:03 +0100 Subject: [PATCH 1/2] Fixed contextual types within generic tuple mapped types --- src/compiler/checker.ts | 9 +- ...ypedElementsOfGenericZippingTuples.symbols | 92 ++++++++++++++++++ ...yTypedElementsOfGenericZippingTuples.types | 94 +++++++++++++++++++ ...allyTypedElementsOfGenericZippingTuples.ts | 44 +++++++++ 4 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/contextuallyTypedElementsOfGenericZippingTuples.symbols create mode 100644 tests/baselines/reference/contextuallyTypedElementsOfGenericZippingTuples.types create mode 100644 tests/cases/compiler/contextuallyTypedElementsOfGenericZippingTuples.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 99071469dec4b..ad4196d2c272e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28943,7 +28943,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !!(getCheckFlags(symbol) & CheckFlags.Mapped && !(symbol as MappedSymbol).links.type && findResolutionCycleStartIndex(symbol, TypeSystemPropertyName.Type) >= 0); } - function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type) { + function getTypeOfPropertyOfContextualType(type: Type, name: __String, nameType?: Type): Type | undefined { return mapType(type, t => { if (isGenericMappedType(t) && !t.declaration.nameType) { const constraint = getConstraintTypeFromMappedType(t); @@ -28959,6 +28959,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop); } if (isTupleType(t) && isNumericLiteralName(name) && +name >= 0) { + if (t.target.hasRestElement) { + const restElement = getTypeArguments(t)[t.target.fixedLength]; + const type = getTypeOfPropertyOfContextualType(restElement, name); + if (type) { + return type; + } + } const restType = getElementTypeOfSliceOfTupleType(t, t.target.fixedLength, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true); if (restType) { return restType; diff --git a/tests/baselines/reference/contextuallyTypedElementsOfGenericZippingTuples.symbols b/tests/baselines/reference/contextuallyTypedElementsOfGenericZippingTuples.symbols new file mode 100644 index 0000000000000..63d63b5017616 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedElementsOfGenericZippingTuples.symbols @@ -0,0 +1,92 @@ +=== tests/cases/compiler/contextuallyTypedElementsOfGenericZippingTuples.ts === +declare function test( +>test : Symbol(test, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 0)) +>T : Symbol(T, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 22)) +>T2 : Symbol(T2, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 42)) + + a: [ +>a : Symbol(a, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 65)) + + ...{ + [K in keyof T]: { +>K : Symbol(K, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 3, 7)) +>T : Symbol(T, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 22)) + + produce: (seed: string) => T[K]; +>produce : Symbol(produce, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 3, 23)) +>seed : Symbol(seed, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 4, 18)) +>T : Symbol(T, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 22)) +>K : Symbol(K, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 3, 7)) + + }; + } + ], + b: [ +>b : Symbol(b, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 7, 4)) + + ...{ + [K in keyof T2]: { +>K : Symbol(K, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 10, 7)) +>T2 : Symbol(T2, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 42)) + + consume: (arg: T[K & keyof T]) => T2[K]; +>consume : Symbol(consume, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 10, 24)) +>arg : Symbol(arg, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 11, 18)) +>T : Symbol(T, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 22)) +>K : Symbol(K, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 10, 7)) +>T : Symbol(T, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 22)) +>T2 : Symbol(T2, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 42)) +>K : Symbol(K, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 10, 7)) + + }; + } + ] +): void; + +test( +>test : Symbol(test, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 0, 0)) + + [ + { + produce: () => "", +>produce : Symbol(produce, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 19, 5)) + + }, + { + produce: () => 42, +>produce : Symbol(produce, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 22, 5)) + + }, + ], + [ + { + consume: (arg) => { +>consume : Symbol(consume, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 27, 5)) +>arg : Symbol(arg, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 28, 16)) + + const received: string = arg; +>received : Symbol(received, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 29, 13)) +>arg : Symbol(arg, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 28, 16)) + + return received; +>received : Symbol(received, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 29, 13)) + + }, + }, + { + consume: (arg) => { +>consume : Symbol(consume, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 33, 5)) +>arg : Symbol(arg, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 34, 16)) + + const received: number = arg; +>received : Symbol(received, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 35, 13)) +>arg : Symbol(arg, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 34, 16)) + + return received; +>received : Symbol(received, Decl(contextuallyTypedElementsOfGenericZippingTuples.ts, 35, 13)) + + }, + }, + ] +); + diff --git a/tests/baselines/reference/contextuallyTypedElementsOfGenericZippingTuples.types b/tests/baselines/reference/contextuallyTypedElementsOfGenericZippingTuples.types new file mode 100644 index 0000000000000..2956613eebab9 --- /dev/null +++ b/tests/baselines/reference/contextuallyTypedElementsOfGenericZippingTuples.types @@ -0,0 +1,94 @@ +=== tests/cases/compiler/contextuallyTypedElementsOfGenericZippingTuples.ts === +declare function test( +>test : (a: [...{ [K in keyof T]: { produce: (seed: string) => T[K]; }; }], b: [...{ [K in keyof T2]: { consume: (arg: T[K & keyof T]) => T2[K]; }; }]) => void + + a: [ +>a : [...{ [K in keyof T]: { produce: (seed: string) => T[K]; }; }] + + ...{ + [K in keyof T]: { + produce: (seed: string) => T[K]; +>produce : (seed: string) => T[K] +>seed : string + + }; + } + ], + b: [ +>b : [...{ [K in keyof T2]: { consume: (arg: T[K & keyof T]) => T2[K]; }; }] + + ...{ + [K in keyof T2]: { + consume: (arg: T[K & keyof T]) => T2[K]; +>consume : (arg: T[K & keyof T]) => T2[K] +>arg : T[K & keyof T] + + }; + } + ] +): void; + +test( +>test( [ { produce: () => "", }, { produce: () => 42, }, ], [ { consume: (arg) => { const received: string = arg; return received; }, }, { consume: (arg) => { const received: number = arg; return received; }, }, ]) : void +>test : (a: [...{ [K in keyof T]: { produce: (seed: string) => T[K]; }; }], b: [...{ [K in keyof T2]: { consume: (arg: T[K & keyof T]) => T2[K]; }; }]) => void + + [ +>[ { produce: () => "", }, { produce: () => 42, }, ] : [{ produce: () => string; }, { produce: () => number; }] + { +>{ produce: () => "", } : { produce: () => string; } + + produce: () => "", +>produce : () => string +>() => "" : () => string +>"" : "" + + }, + { +>{ produce: () => 42, } : { produce: () => number; } + + produce: () => 42, +>produce : () => number +>() => 42 : () => number +>42 : 42 + + }, + ], + [ +>[ { consume: (arg) => { const received: string = arg; return received; }, }, { consume: (arg) => { const received: number = arg; return received; }, }, ] : [{ consume: (arg: string) => string; }, { consume: (arg: number) => number; }] + { +>{ consume: (arg) => { const received: string = arg; return received; }, } : { consume: (arg: string) => string; } + + consume: (arg) => { +>consume : (arg: string) => string +>(arg) => { const received: string = arg; return received; } : (arg: string) => string +>arg : string + + const received: string = arg; +>received : string +>arg : string + + return received; +>received : string + + }, + }, + { +>{ consume: (arg) => { const received: number = arg; return received; }, } : { consume: (arg: number) => number; } + + consume: (arg) => { +>consume : (arg: number) => number +>(arg) => { const received: number = arg; return received; } : (arg: number) => number +>arg : number + + const received: number = arg; +>received : number +>arg : number + + return received; +>received : number + + }, + }, + ] +); + diff --git a/tests/cases/compiler/contextuallyTypedElementsOfGenericZippingTuples.ts b/tests/cases/compiler/contextuallyTypedElementsOfGenericZippingTuples.ts new file mode 100644 index 0000000000000..a011321a263cc --- /dev/null +++ b/tests/cases/compiler/contextuallyTypedElementsOfGenericZippingTuples.ts @@ -0,0 +1,44 @@ +// @strict: true +// @noEmit: true + +declare function test( + a: [ + ...{ + [K in keyof T]: { + produce: (seed: string) => T[K]; + }; + } + ], + b: [ + ...{ + [K in keyof T2]: { + consume: (arg: T[K & keyof T]) => T2[K]; + }; + } + ] +): void; + +test( + [ + { + produce: () => "", + }, + { + produce: () => 42, + }, + ], + [ + { + consume: (arg) => { + const received: string = arg; + return received; + }, + }, + { + consume: (arg) => { + const received: number = arg; + return received; + }, + }, + ] +); From d4b516dd33868b655a1c806e9d3b2b9c89d34187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 1 Mar 2023 13:11:50 +0100 Subject: [PATCH 2/2] Avoid infinite recusion with recursive tuple type references --- src/compiler/checker.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad4196d2c272e..02a435ab1cf84 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28961,9 +28961,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isTupleType(t) && isNumericLiteralName(name) && +name >= 0) { if (t.target.hasRestElement) { const restElement = getTypeArguments(t)[t.target.fixedLength]; - const type = getTypeOfPropertyOfContextualType(restElement, name); - if (type) { - return type; + if (restElement !== type) { + const propType = getTypeOfPropertyOfContextualType(restElement, name); + if (propType) { + return propType; + } } } const restType = getElementTypeOfSliceOfTupleType(t, t.target.fixedLength, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true);