Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -28959,6 +28959,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return isCircularMappedProperty(prop) ? undefined : getTypeOfSymbol(prop);
}
if (isTupleType(t) && isNumericLiteralName(name) && +name >= 0) {
if (t.target.hasRestElement) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tuple type could be [...T] where T extends [] and, thus, should not have a contextual type for a numbered element (at all), so I'm not sure checking this field is correct.

const restElement = getTypeArguments(t)[t.target.fixedLength];
if (restElement !== type) {
const propType = getTypeOfPropertyOfContextualType(restElement, name);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Erm, name needs to be offset by the length of the preceding fixed elements of the tuple, no? And even that's thrown into question with multiple variadic tuple elements? Eg, if t is [...T, ...U] and name is 0, where T extends [] | [number] and U extends [] | [string], this is just going to lookup 0 on T, which is, at best, a partial result. (You'd want the contextual type to be something like string | number | undefined or something constrained to such). Seems like something a bit more complete is needed to handle variadic tuples fully.

if (propType) {
return propType;
}
}
}
const restType = getElementTypeOfSliceOfTupleType(t, t.target.fixedLength, /*endSkipCount*/ 0, /*writing*/ false, /*noReductions*/ true);
if (restType) {
return restType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
=== tests/cases/compiler/contextuallyTypedElementsOfGenericZippingTuples.ts ===
declare function test<T extends unknown[], T2 extends unknown[]>(
>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))

},
},
]
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
=== tests/cases/compiler/contextuallyTypedElementsOfGenericZippingTuples.ts ===
declare function test<T extends unknown[], T2 extends unknown[]>(
>test : <T extends unknown[], T2 extends unknown[]>(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 : <T extends unknown[], T2 extends unknown[]>(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

},
},
]
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// @strict: true
// @noEmit: true

declare function test<T extends unknown[], T2 extends unknown[]>(
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;
},
},
]
);