我已经写了Omit
的几种实现,包括Intellisense悬停在Omit
本身上时显示的实现。我很难理解为什么某些实现是同态的而其他实现不是。
我发现:
Omit
上时显示的实现不正确Omit
上时显示的实现不会保留属性的“可选性”(即不是同态的),因此与保留“可选性”的实际实现不同。这是我的代码:
// a type with optional and readonly properties
type HasOptional = { a: number; b?: number, c: string; d?: readonly string[]; };
// first attempt
type Omit_1<T, K extends keyof T> = { [P in Exclude<keyof T, K>]: T[P]; };
type Omit_1_Optional = Omit_1<HasOptional, 'a'>; // b, d lost optionality
// Omit's 'fake' implementation, as shown by Intellisense
type Omit_2<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; };
type Omit_2_Optional = Omit_2<HasOptional, 'a'>; // b, d lost optionality
// Using Omit itself
type Omit_3<T, K extends string | number | symbol> = Omit<T, K>;
type Omit_3_Optional = Omit_3<HasOptional, 'a'>; // optionality maintained!
// Writing Omit's implementation explicitly
type Omit_4<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Omit_4_Optional = Omit_4<HasOptional, 'a'>; // optionality maintained!
我已经见过here, in an answer about deep Omit,[P in K]:
被用作导致同态行为的间接附加级别,但是这里也存在,但是前两个实现并未保留“可选性”。
答案 0 :(得分:1)
在两种情况下,映射类型被视为同态。我们要么映射到keyof T
(docs)上,要么映射到类型参数K
,其中K
的约束为keyof T
(K extends keyof T
, docs)。
虽然Exclude<keyof T, K>
确实扩展了keyof T
,但不适合这两种特定情况。这意味着直接在Exclude<keyof T, K>
上映射将不会产生同态映射类型。如果我们采用Exclude<keyof T, K>
并将其放入具有所需约束的类型参数中,则会得到所需的行为。
// a type with optional and readonly properties
type HasOptional = { a: number; b?: number, c: string; d?: readonly string[]; };
// mapping over Exclude<keyof T, K> optionality lost
type Omit_1<T, K extends keyof T> = { [P in Exclude<keyof T, K>]: T[P]; };
type Omit_1_Optional = Omit_1<HasOptional, 'a'>; // b, d lost optionality
// mapping over Exclude<keyof T, K> optionality lost
type Omit_2<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; };
type Omit_2_Optional = Omit_2<HasOptional, 'a'>; // b, d lost optionality
// Omit in 3.5 has homomorphic behavior since it uses Pick which is homomorphic
type Omit_3<T, K extends string | number | symbol> = Omit<T, K>;
type Omit_3_Optional = Omit_3<HasOptional, 'a'>; // optionality maintained!
// has homomorphic behavior since it uses Pick which is homomorphic
type Omit_4<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Omit_4_Optional = Omit_4<HasOptional, 'a'>; // optionality maintained!