Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve contextual types for elements typed by reverse mapped tuples #60901

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
15 changes: 11 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31877,14 +31877,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// When the length is known and the index is after all spread elements we compute the offset from the element
// to the end and the number of ending fixed elements in the contextual tuple type.
const offset = length !== undefined && (lastSpreadIndex === undefined || index > lastSpreadIndex) ? length - index : 0;
const fixedEndLength = offset > 0 && (t.target.combinedFlags & ElementFlags.Variable) ? getEndElementCount(t.target, ElementFlags.Fixed) : 0;
const fixedEndLength = getEndElementCount(t.target, ElementFlags.Fixed);
const endSkipCount = offset > 0 && (t.target.combinedFlags & ElementFlags.Variable) ? fixedEndLength : 0;
const arity = getTypeReferenceArity(t);
// If the offset is within the ending fixed part of the contextual tuple type, return the type of the contextual
// tuple element.
if (offset > 0 && offset <= fixedEndLength) {
return getTypeArguments(t)[getTypeReferenceArity(t) - offset];
if (offset > 0 && offset <= endSkipCount) {
return getTypeArguments(t)[arity - offset];
}
const middleLength = arity - t.target.fixedLength - fixedEndLength;
const singleVariadicIndex = middleLength === 1 && t.target.elementFlags[t.target.fixedLength] & ElementFlags.Variadic ? t.target.fixedLength : -1;
if (singleVariadicIndex !== -1 && (firstSpreadIndex === undefined || index < firstSpreadIndex)) {
return getIndexedAccessType(getTypeArguments(t)[singleVariadicIndex], getStringLiteralType("" + (index - singleVariadicIndex)));
}
// Return a union of the possible contextual element types with no subtype reduction.
return getElementTypeOfSliceOfTupleType(t, firstSpreadIndex === undefined ? t.target.fixedLength : Math.min(t.target.fixedLength, firstSpreadIndex), length === undefined || lastSpreadIndex === undefined ? fixedEndLength : Math.min(fixedEndLength, length - lastSpreadIndex), /*writing*/ false, /*noReductions*/ true);
return getElementTypeOfSliceOfTupleType(t, firstSpreadIndex === undefined ? t.target.fixedLength : Math.min(t.target.fixedLength, firstSpreadIndex), length === undefined || lastSpreadIndex === undefined ? endSkipCount : Math.min(endSkipCount, length - lastSpreadIndex), /*writing*/ false, /*noReductions*/ true);
}
// If element index is known and a contextual property with that name exists, return it. Otherwise return the
// iterated or element type of the contextual type.
Expand Down
215 changes: 215 additions & 0 deletions tests/baselines/reference/reverseMappedTuples1.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
reverseMappedTuples1.ts(39,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(44,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(63,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(68,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(69,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(97,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(112,4): error TS2322: Type 'typeof Bar' is not assignable to type 'Constructor<Foo | undefined>'.
Property 'test' is missing in type 'Bar' but required in type 'Foo'.
reverseMappedTuples1.ts(126,4): error TS2322: Type 'typeof Baz' is not assignable to type 'Constructor<Foo | undefined>'.
Property 'test' is missing in type 'Baz' but required in type 'Foo'.
reverseMappedTuples1.ts(145,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(146,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(147,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(156,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(157,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(158,15): error TS7006: Parameter 't' implicitly has an 'any' type.
reverseMappedTuples1.ts(159,15): error TS7006: Parameter 't' implicitly has an 'any' type.


==== reverseMappedTuples1.ts (15 errors) ====
// https://github.com/microsoft/TypeScript/issues/58726

type Constructor<T> = {
new (...args: never[]): T;
};

interface GenericParams<T> {
bar: (t: T) => void;
}

type Pair<T> = [Constructor<T>, GenericParams<T>];

type List<T> = {
[K in keyof T]: Pair<T[K]>;
};

class Foo {
test() {}
}
class Bar {
other() {}
}
class Baz {
third() {}
}

declare const withFooPair: [Pair<Foo>];
declare const with2FooPairs: [Pair<Foo>, Pair<Foo>];

declare function fn1<T extends readonly {}[]>(params: List<[...T]>): T;

const res1 = fn1([
[Foo, { bar(t) {} }],
[Bar, { bar(t) {} }],
]);

const res2 = fn1([
...withFooPair,
[Bar, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
]);

const res3 = fn1([
...with2FooPairs,
[Bar, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
]);

const res4 = fn1([
[Bar, { bar(t) {} }],
...with2FooPairs,
]);

declare function fn2<T extends readonly {}[]>(params: List<[Foo, Foo, ...T]>): T;

const res5 = fn2([
[Foo, { bar(t) {} }],
[Foo, { bar(t) {} }],
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }],
]);

const res6 = fn2([
...with2FooPairs,
[Bar, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
]);

const res7 = fn2([
...with2FooPairs,
[Bar, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
[Baz, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
]);

declare function fn3<T extends readonly {}[]>(params: List<[...T, Foo, Foo]>): T;

const res8 = fn3([
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }],
[Foo, { bar(t) {} }],
[Foo, { bar(t) {} }],
]);

const res9 = fn3([
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }],
...with2FooPairs
]);

const res10 = fn3([
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }],
...withFooPair,
[Foo, { bar(t) {} }],
]);

const res11 = fn3([
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }],
[Foo, { bar(t) {} }],
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
...withFooPair,
]);

declare function fn4<T extends readonly {}[]>(params: List<[Foo, Foo?, ...T]>): T;

const res12 = fn4([
[Foo, { bar(t) {} }],
[Foo, { bar(t) {} }],
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }],
]);

const res13 = fn4([
[Foo, { bar(t) {} }],
[Bar, { bar(t) {} }], // error
~~~
!!! error TS2322: Type 'typeof Bar' is not assignable to type 'Constructor<Foo | undefined>'.
!!! error TS2322: Property 'test' is missing in type 'Bar' but required in type 'Foo'.
!!! related TS2728 reverseMappedTuples1.ts:18:3: 'test' is declared here.
[Baz, { bar(t) {} }],
]);

declare function fn5<T extends readonly {}[]>(params: List<[...T, Foo?]>): T;

const res14 = fn5([
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }],
[Foo, { bar(t) {} }],
]);

const res15 = fn5([
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }], // error, inferred [Bar, Baz] would satisfy but the checker prefers picking up trailing optional element for contextual typing etc
~~~
!!! error TS2322: Type 'typeof Baz' is not assignable to type 'Constructor<Foo | undefined>'.
!!! error TS2322: Property 'test' is missing in type 'Baz' but required in type 'Foo'.
!!! related TS2728 reverseMappedTuples1.ts:18:3: 'test' is declared here.
]);

declare function fn6<T extends readonly {}[]>(params: List<[Foo, Foo, ...T, Foo, Foo]>): T;

const res16 = fn6([
[Foo, { bar(t) {} }],
[Foo, { bar(t) {} }],
[Bar, { bar(t) {} }],
[Baz, { bar(t) {} }],
[Foo, { bar(t) {} }],
[Foo, { bar(t) {} }],
]);

declare function fn7<T extends readonly {}[], T2 extends readonly {}[]>(params: List<[Foo, Foo, ...T, ...T2, Foo]>): [T, T2];

const res17 = fn7([
[Foo, { bar(t) {} }],
[Foo, { bar(t) {} }],
[Bar, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
[Baz, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
[Foo, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
[Foo, { bar(t) {} }],
]);

declare function fn8<T extends readonly {}[]>(params: List<[Foo, Foo, ...T, ...Foo[]]>): T;

const res18 = fn8([
[Foo, { bar(t) {} }],
[Foo, { bar(t) {} }],
[Bar, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
[Baz, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
[Foo, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
[Foo, { bar(t) {} }], // implicit any
~
!!! error TS7006: Parameter 't' implicitly has an 'any' type.
]);

Loading
Loading