Distribute indexes of indexed access types first (#27243)
This commit is contained in:
parent
2b607a6ed0
commit
219bb44b4d
6 changed files with 154 additions and 6 deletions
|
@ -9369,12 +9369,24 @@ namespace ts {
|
|||
// '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type.
|
||||
const objectType = getSimplifiedType(type.objectType);
|
||||
const indexType = getSimplifiedType(type.indexType);
|
||||
if (objectType.flags & TypeFlags.Union) {
|
||||
return type.simplified = mapType(objectType, t => getSimplifiedType(getIndexedAccessType(t, indexType)));
|
||||
// T[A | B] -> T[A] | T[B]
|
||||
if (indexType.flags & TypeFlags.Union) {
|
||||
return type.simplified = mapType(indexType, t => getSimplifiedType(getIndexedAccessType(objectType, t)));
|
||||
}
|
||||
if (objectType.flags & TypeFlags.Intersection) {
|
||||
return type.simplified = getIntersectionType(map((objectType as IntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType))));
|
||||
// Only do the inner distributions if the index can no longer be instantiated to cause index distribution again
|
||||
if (!(indexType.flags & TypeFlags.Instantiable)) {
|
||||
// (T | U)[K] -> T[K] | U[K]
|
||||
if (objectType.flags & TypeFlags.Union) {
|
||||
return type.simplified = mapType(objectType, t => getSimplifiedType(getIndexedAccessType(t, indexType)));
|
||||
}
|
||||
// (T & U)[K] -> T[K] & U[K]
|
||||
if (objectType.flags & TypeFlags.Intersection) {
|
||||
return type.simplified = getIntersectionType(map((objectType as IntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType))));
|
||||
}
|
||||
}
|
||||
// So ultimately:
|
||||
// ((A & B) | C)[K1 | K2] -> ((A & B) | C)[K1] | ((A & B) | C)[K2] -> (A & B)[K1] | C[K1] | (A & B)[K2] | C[K2] -> (A[K1] & B[K1]) | C[K1] | (A[K2] & B[K2]) | C[K2]
|
||||
|
||||
// If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper
|
||||
// that substitutes the index type for P. For example, for an index access { [P in K]: Box<T[P]> }[X], we
|
||||
// construct the type Box<T[X]>. We do not further simplify the result because mapped types can be recursive
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
//// [contextualTypeOfIndexedAccessParameter.ts]
|
||||
type Keys = "a" | "b";
|
||||
|
||||
type OptionsForKey = { a: { cb: (p: number) => number } } & { b: {} };
|
||||
|
||||
declare function f<K extends Keys>(key: K, options: OptionsForKey[K]): void;
|
||||
|
||||
f("a", {
|
||||
cb: p => p,
|
||||
});
|
||||
|
||||
function g<
|
||||
K extends "a" | "b">(x: ({ a: string } & { b: string })[K], y: string) {
|
||||
x = y;
|
||||
}
|
||||
|
||||
|
||||
//// [contextualTypeOfIndexedAccessParameter.js]
|
||||
"use strict";
|
||||
f("a", {
|
||||
cb: function (p) { return p; }
|
||||
});
|
||||
function g(x, y) {
|
||||
x = y;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
=== tests/cases/compiler/contextualTypeOfIndexedAccessParameter.ts ===
|
||||
type Keys = "a" | "b";
|
||||
>Keys : Symbol(Keys, Decl(contextualTypeOfIndexedAccessParameter.ts, 0, 0))
|
||||
|
||||
type OptionsForKey = { a: { cb: (p: number) => number } } & { b: {} };
|
||||
>OptionsForKey : Symbol(OptionsForKey, Decl(contextualTypeOfIndexedAccessParameter.ts, 0, 22))
|
||||
>a : Symbol(a, Decl(contextualTypeOfIndexedAccessParameter.ts, 2, 22))
|
||||
>cb : Symbol(cb, Decl(contextualTypeOfIndexedAccessParameter.ts, 2, 27))
|
||||
>p : Symbol(p, Decl(contextualTypeOfIndexedAccessParameter.ts, 2, 33))
|
||||
>b : Symbol(b, Decl(contextualTypeOfIndexedAccessParameter.ts, 2, 61))
|
||||
|
||||
declare function f<K extends Keys>(key: K, options: OptionsForKey[K]): void;
|
||||
>f : Symbol(f, Decl(contextualTypeOfIndexedAccessParameter.ts, 2, 70))
|
||||
>K : Symbol(K, Decl(contextualTypeOfIndexedAccessParameter.ts, 4, 19))
|
||||
>Keys : Symbol(Keys, Decl(contextualTypeOfIndexedAccessParameter.ts, 0, 0))
|
||||
>key : Symbol(key, Decl(contextualTypeOfIndexedAccessParameter.ts, 4, 35))
|
||||
>K : Symbol(K, Decl(contextualTypeOfIndexedAccessParameter.ts, 4, 19))
|
||||
>options : Symbol(options, Decl(contextualTypeOfIndexedAccessParameter.ts, 4, 42))
|
||||
>OptionsForKey : Symbol(OptionsForKey, Decl(contextualTypeOfIndexedAccessParameter.ts, 0, 22))
|
||||
>K : Symbol(K, Decl(contextualTypeOfIndexedAccessParameter.ts, 4, 19))
|
||||
|
||||
f("a", {
|
||||
>f : Symbol(f, Decl(contextualTypeOfIndexedAccessParameter.ts, 2, 70))
|
||||
|
||||
cb: p => p,
|
||||
>cb : Symbol(cb, Decl(contextualTypeOfIndexedAccessParameter.ts, 6, 8))
|
||||
>p : Symbol(p, Decl(contextualTypeOfIndexedAccessParameter.ts, 7, 7))
|
||||
>p : Symbol(p, Decl(contextualTypeOfIndexedAccessParameter.ts, 7, 7))
|
||||
|
||||
});
|
||||
|
||||
function g<
|
||||
>g : Symbol(g, Decl(contextualTypeOfIndexedAccessParameter.ts, 8, 3))
|
||||
|
||||
K extends "a" | "b">(x: ({ a: string } & { b: string })[K], y: string) {
|
||||
>K : Symbol(K, Decl(contextualTypeOfIndexedAccessParameter.ts, 10, 11))
|
||||
>x : Symbol(x, Decl(contextualTypeOfIndexedAccessParameter.ts, 11, 25))
|
||||
>a : Symbol(a, Decl(contextualTypeOfIndexedAccessParameter.ts, 11, 30))
|
||||
>b : Symbol(b, Decl(contextualTypeOfIndexedAccessParameter.ts, 11, 46))
|
||||
>K : Symbol(K, Decl(contextualTypeOfIndexedAccessParameter.ts, 10, 11))
|
||||
>y : Symbol(y, Decl(contextualTypeOfIndexedAccessParameter.ts, 11, 63))
|
||||
|
||||
x = y;
|
||||
>x : Symbol(x, Decl(contextualTypeOfIndexedAccessParameter.ts, 11, 25))
|
||||
>y : Symbol(y, Decl(contextualTypeOfIndexedAccessParameter.ts, 11, 63))
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
=== tests/cases/compiler/contextualTypeOfIndexedAccessParameter.ts ===
|
||||
type Keys = "a" | "b";
|
||||
>Keys : Keys
|
||||
|
||||
type OptionsForKey = { a: { cb: (p: number) => number } } & { b: {} };
|
||||
>OptionsForKey : OptionsForKey
|
||||
>a : { cb: (p: number) => number; }
|
||||
>cb : (p: number) => number
|
||||
>p : number
|
||||
>b : {}
|
||||
|
||||
declare function f<K extends Keys>(key: K, options: OptionsForKey[K]): void;
|
||||
>f : <K extends Keys>(key: K, options: OptionsForKey[K]) => void
|
||||
>key : K
|
||||
>options : OptionsForKey[K]
|
||||
|
||||
f("a", {
|
||||
>f("a", { cb: p => p,}) : void
|
||||
>f : <K extends Keys>(key: K, options: OptionsForKey[K]) => void
|
||||
>"a" : "a"
|
||||
>{ cb: p => p,} : { cb: (p: number) => number; }
|
||||
|
||||
cb: p => p,
|
||||
>cb : (p: number) => number
|
||||
>p => p : (p: number) => number
|
||||
>p : number
|
||||
>p : number
|
||||
|
||||
});
|
||||
|
||||
function g<
|
||||
>g : <K extends Keys>(x: ({ a: string; } & { b: string; })[K], y: string) => void
|
||||
|
||||
K extends "a" | "b">(x: ({ a: string } & { b: string })[K], y: string) {
|
||||
>x : ({ a: string; } & { b: string; })[K]
|
||||
>a : string
|
||||
>b : string
|
||||
>y : string
|
||||
|
||||
x = y;
|
||||
>x = y : string
|
||||
>x : ({ a: string; } & { b: string; })[K]
|
||||
>y : string
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, string | number | symbol>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, string | number | symbol>>], Record<"val", string>>["val"]'.
|
||||
error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, number>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, number>>], Record<"val", string>>["val"]'.
|
||||
error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, string>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, string>>], Record<"val", string>>["val"]'.
|
||||
error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, symbol>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, symbol>>], Record<"val", string>>["val"]'.
|
||||
tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
|
||||
tests/cases/compiler/infiniteConstraints.ts(27,37): error TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
|
||||
tests/cases/compiler/infiniteConstraints.ts(27,58): error TS2322: Type 'Record<"val", "test2">' is not assignable to type 'never'.
|
||||
|
@ -8,7 +10,9 @@ tests/cases/compiler/infiniteConstraints.ts(31,63): error TS2322: Type 'Record<"
|
|||
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
|
||||
|
||||
|
||||
!!! error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, string | number | symbol>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, string | number | symbol>>], Record<"val", string>>["val"]'.
|
||||
!!! error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, number>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, number>>], Record<"val", string>>["val"]'.
|
||||
!!! error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, string>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, string>>], Record<"val", string>>["val"]'.
|
||||
!!! error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, symbol>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, symbol>>], Record<"val", string>>["val"]'.
|
||||
==== tests/cases/compiler/infiniteConstraints.ts (7 errors) ====
|
||||
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
// @strict: true
|
||||
type Keys = "a" | "b";
|
||||
|
||||
type OptionsForKey = { a: { cb: (p: number) => number } } & { b: {} };
|
||||
|
||||
declare function f<K extends Keys>(key: K, options: OptionsForKey[K]): void;
|
||||
|
||||
f("a", {
|
||||
cb: p => p,
|
||||
});
|
||||
|
||||
function g<
|
||||
K extends "a" | "b">(x: ({ a: string } & { b: string })[K], y: string) {
|
||||
x = y;
|
||||
}
|
Loading…
Reference in a new issue