Typescript-一些省略的实现从属性中删除了可选性

时间:2019-05-11 17:49:35

标签: typescript

我已经写了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]:被用作导致同态行为的间接附加级别,但是这里也存在,但是前两个实现并未保留“可选性”。

1 个答案:

答案 0 :(得分:1)

在两种情况下,映射类型被视为同态。我们要么映射到keyof Tdocs)上,要么映射到类型参数K,其中K的约束为keyof TK extends keyof Tdocs)。

虽然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!