mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 12:51:30 -05:00
Merge pull request #21156 from Microsoft/fix-diff-omit
Fix Diff and Omit
This commit is contained in:
@@ -8359,30 +8359,45 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isMappedTypeToNever(type: Type) {
|
||||
return getObjectFlags(type) & ObjectFlags.Mapped && getTemplateTypeFromMappedType(type as MappedType) === neverType;
|
||||
}
|
||||
|
||||
// Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
|
||||
// undefined if no transformation is possible.
|
||||
function getSimplifiedIndexedAccessType(type: IndexedAccessType): Type {
|
||||
const objectType = type.objectType;
|
||||
// Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
|
||||
// more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
|
||||
// transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
|
||||
// access types with default property values as expressed by D.
|
||||
if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType) && some((<IntersectionType>objectType).types, isStringIndexOnlyType)) {
|
||||
const regularTypes: Type[] = [];
|
||||
const stringIndexTypes: Type[] = [];
|
||||
for (const t of (<IntersectionType>objectType).types) {
|
||||
if (isStringIndexOnlyType(t)) {
|
||||
stringIndexTypes.push(getIndexTypeOfType(t, IndexKind.String));
|
||||
}
|
||||
else {
|
||||
regularTypes.push(t);
|
||||
if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType)) {
|
||||
// Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
|
||||
// more object types with only a string index signature, e.g. '(U & V & { [x: string]: D })[K]', return a
|
||||
// transformed type of the form '(U & V)[K] | D'. This allows us to properly reason about higher order indexed
|
||||
// access types with default property values as expressed by D.
|
||||
if (some((<IntersectionType>objectType).types, isStringIndexOnlyType)) {
|
||||
const regularTypes: Type[] = [];
|
||||
const stringIndexTypes: Type[] = [];
|
||||
for (const t of (<IntersectionType>objectType).types) {
|
||||
if (isStringIndexOnlyType(t)) {
|
||||
stringIndexTypes.push(getIndexTypeOfType(t, IndexKind.String));
|
||||
}
|
||||
else {
|
||||
regularTypes.push(t);
|
||||
}
|
||||
}
|
||||
return getUnionType([
|
||||
getIndexedAccessType(getIntersectionType(regularTypes), type.indexType),
|
||||
getIntersectionType(stringIndexTypes)
|
||||
]);
|
||||
}
|
||||
// Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
|
||||
// more mapped types with a template type `never`, '(U & V & { [P in T]: never })[K]', return a
|
||||
// transformed type that removes the never-mapped type: '(U & V)[K]'. This mirrors what would happen
|
||||
// eventually anyway, but it easier to reason about.
|
||||
if (some((<IntersectionType>objectType).types, isMappedTypeToNever)) {
|
||||
const nonNeverTypes = filter((<IntersectionType>objectType).types, t => !isMappedTypeToNever(t));
|
||||
return getIndexedAccessType(getIntersectionType(nonNeverTypes), type.indexType);
|
||||
}
|
||||
return getUnionType([
|
||||
getIndexedAccessType(getIntersectionType(regularTypes), type.indexType),
|
||||
getIntersectionType(stringIndexTypes)
|
||||
]);
|
||||
}
|
||||
|
||||
// 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]>.
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//// [indexedAccessRetainsIndexSignature.ts]
|
||||
type Diff<T extends string, U extends string> =
|
||||
({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]
|
||||
type Omit<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>
|
||||
type Omit1<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>;
|
||||
// is in fact an equivalent of
|
||||
|
||||
type Omit2<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
|
||||
|
||||
type O = Omit<{ a: number, b: string }, 'a'>
|
||||
const o: O = { b: '' }
|
||||
|
||||
|
||||
//// [indexedAccessRetainsIndexSignature.js]
|
||||
var o = { b: '' };
|
||||
@@ -0,0 +1,62 @@
|
||||
=== tests/cases/compiler/indexedAccessRetainsIndexSignature.ts ===
|
||||
type Diff<T extends string, U extends string> =
|
||||
>Diff : Symbol(Diff, Decl(indexedAccessRetainsIndexSignature.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(indexedAccessRetainsIndexSignature.ts, 0, 10))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 0, 27))
|
||||
|
||||
({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]
|
||||
>P : Symbol(P, Decl(indexedAccessRetainsIndexSignature.ts, 1, 8))
|
||||
>T : Symbol(T, Decl(indexedAccessRetainsIndexSignature.ts, 0, 10))
|
||||
>P : Symbol(P, Decl(indexedAccessRetainsIndexSignature.ts, 1, 8))
|
||||
>P : Symbol(P, Decl(indexedAccessRetainsIndexSignature.ts, 1, 26))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 0, 27))
|
||||
>x : Symbol(x, Decl(indexedAccessRetainsIndexSignature.ts, 1, 48))
|
||||
>T : Symbol(T, Decl(indexedAccessRetainsIndexSignature.ts, 0, 10))
|
||||
|
||||
type Omit<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>
|
||||
>Omit : Symbol(Omit, Decl(indexedAccessRetainsIndexSignature.ts, 1, 71))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 2, 10))
|
||||
>K : Symbol(K, Decl(indexedAccessRetainsIndexSignature.ts, 2, 12))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 2, 10))
|
||||
>Pick : Symbol(Pick, Decl(lib.d.ts, --, --))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 2, 10))
|
||||
>Diff : Symbol(Diff, Decl(indexedAccessRetainsIndexSignature.ts, 0, 0))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 2, 10))
|
||||
>K : Symbol(K, Decl(indexedAccessRetainsIndexSignature.ts, 2, 12))
|
||||
|
||||
type Omit1<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>;
|
||||
>Omit1 : Symbol(Omit1, Decl(indexedAccessRetainsIndexSignature.ts, 2, 59))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 3, 11))
|
||||
>K : Symbol(K, Decl(indexedAccessRetainsIndexSignature.ts, 3, 13))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 3, 11))
|
||||
>Pick : Symbol(Pick, Decl(lib.d.ts, --, --))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 3, 11))
|
||||
>Diff : Symbol(Diff, Decl(indexedAccessRetainsIndexSignature.ts, 0, 0))
|
||||
>U : Symbol(U, Decl(indexedAccessRetainsIndexSignature.ts, 3, 11))
|
||||
>K : Symbol(K, Decl(indexedAccessRetainsIndexSignature.ts, 3, 13))
|
||||
|
||||
// is in fact an equivalent of
|
||||
|
||||
type Omit2<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
|
||||
>Omit2 : Symbol(Omit2, Decl(indexedAccessRetainsIndexSignature.ts, 3, 61))
|
||||
>T : Symbol(T, Decl(indexedAccessRetainsIndexSignature.ts, 6, 11))
|
||||
>K : Symbol(K, Decl(indexedAccessRetainsIndexSignature.ts, 6, 13))
|
||||
>T : Symbol(T, Decl(indexedAccessRetainsIndexSignature.ts, 6, 11))
|
||||
>P : Symbol(P, Decl(indexedAccessRetainsIndexSignature.ts, 6, 37))
|
||||
>Diff : Symbol(Diff, Decl(indexedAccessRetainsIndexSignature.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(indexedAccessRetainsIndexSignature.ts, 6, 11))
|
||||
>K : Symbol(K, Decl(indexedAccessRetainsIndexSignature.ts, 6, 13))
|
||||
>T : Symbol(T, Decl(indexedAccessRetainsIndexSignature.ts, 6, 11))
|
||||
>P : Symbol(P, Decl(indexedAccessRetainsIndexSignature.ts, 6, 37))
|
||||
|
||||
type O = Omit<{ a: number, b: string }, 'a'>
|
||||
>O : Symbol(O, Decl(indexedAccessRetainsIndexSignature.ts, 6, 67))
|
||||
>Omit : Symbol(Omit, Decl(indexedAccessRetainsIndexSignature.ts, 1, 71))
|
||||
>a : Symbol(a, Decl(indexedAccessRetainsIndexSignature.ts, 8, 15))
|
||||
>b : Symbol(b, Decl(indexedAccessRetainsIndexSignature.ts, 8, 26))
|
||||
|
||||
const o: O = { b: '' }
|
||||
>o : Symbol(o, Decl(indexedAccessRetainsIndexSignature.ts, 9, 5))
|
||||
>O : Symbol(O, Decl(indexedAccessRetainsIndexSignature.ts, 6, 67))
|
||||
>b : Symbol(b, Decl(indexedAccessRetainsIndexSignature.ts, 9, 14))
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
=== tests/cases/compiler/indexedAccessRetainsIndexSignature.ts ===
|
||||
type Diff<T extends string, U extends string> =
|
||||
>Diff : ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[T]
|
||||
>T : T
|
||||
>U : U
|
||||
|
||||
({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]
|
||||
>P : P
|
||||
>T : T
|
||||
>P : P
|
||||
>P : P
|
||||
>U : U
|
||||
>x : string
|
||||
>T : T
|
||||
|
||||
type Omit<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>
|
||||
>Omit : Pick<U, ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[keyof U]>
|
||||
>U : U
|
||||
>K : K
|
||||
>U : U
|
||||
>Pick : Pick<T, K>
|
||||
>U : U
|
||||
>Diff : ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[T]
|
||||
>U : U
|
||||
>K : K
|
||||
|
||||
type Omit1<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>;
|
||||
>Omit1 : Pick<U, ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[keyof U]>
|
||||
>U : U
|
||||
>K : K
|
||||
>U : U
|
||||
>Pick : Pick<T, K>
|
||||
>U : U
|
||||
>Diff : ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[T]
|
||||
>U : U
|
||||
>K : K
|
||||
|
||||
// is in fact an equivalent of
|
||||
|
||||
type Omit2<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
|
||||
>Omit2 : Omit2<T, K>
|
||||
>T : T
|
||||
>K : K
|
||||
>T : T
|
||||
>P : P
|
||||
>Diff : ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[T]
|
||||
>T : T
|
||||
>K : K
|
||||
>T : T
|
||||
>P : P
|
||||
|
||||
type O = Omit<{ a: number, b: string }, 'a'>
|
||||
>O : Pick<{ a: number; b: string; }, "b">
|
||||
>Omit : Pick<U, ({ [P in T]: P; } & { [P in U]: never; } & { [x: string]: never; })[keyof U]>
|
||||
>a : number
|
||||
>b : string
|
||||
|
||||
const o: O = { b: '' }
|
||||
>o : Pick<{ a: number; b: string; }, "b">
|
||||
>O : Pick<{ a: number; b: string; }, "b">
|
||||
>{ b: '' } : { b: string; }
|
||||
>b : string
|
||||
>'' : ""
|
||||
|
||||
10
tests/cases/compiler/indexedAccessRetainsIndexSignature.ts
Normal file
10
tests/cases/compiler/indexedAccessRetainsIndexSignature.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
type Diff<T extends string, U extends string> =
|
||||
({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T]
|
||||
type Omit<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>
|
||||
type Omit1<U, K extends keyof U> = Pick<U, Diff<keyof U, K>>;
|
||||
// is in fact an equivalent of
|
||||
|
||||
type Omit2<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
|
||||
|
||||
type O = Omit<{ a: number, b: string }, 'a'>
|
||||
const o: O = { b: '' }
|
||||
Reference in New Issue
Block a user