1

定义这样的接口

interface Props {
  a?: string
  b?: string
  c?: string
  d: string
}

当我做这样的事情时,没关系

type Test1 = {
  [P in keyof Props]: Props[P]
}

const a1: Test1 = {
  d: '1'
}

但是,像这样,我收到了一些错误消息

type Keys = keyof Props;

type Test1 = {
  [P in Keys]: Props[P]
}

// error Type '{ d: string; }' is missing the following properties from type 'Test1': a, b, c(2739)

const a1: Test1 = {
  d: '1'
}

另一个

type MyOmit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P]; }

// Property 'a' is missing in type '{ d: string; }' but required in type 'MyOmit<Props, "b" | "c">'.
const o1: MyOmit<Props, 'b' | 'c'> = {
  d: '1'
}

// Its Ok!
const o2: Omit<Props, 'b' | 'c'> = {
  d: '1'
}

为什么 MyOmit 会出错?

4

2 回答 2

1

type Test1 = { [P in keyof Props]: Props[P] }和之间存在巨大差异type Test2 = { [P in Keys]: Props[P] }

见第一个: 在此处输入图像描述

每个属性都是可选的。

第二个: 在此处输入图像描述

每个属性都是必需的,但可能是undefined.

请看这个标志。

TS 之所以采用这种方式,是因为 JS 的动态特性。

考虑这个例子:

const foo = { name: undefined }
foo.name // undefined
foo.surname // undefined

undefined如果您试图获取不存在的属性,JS 将返回。您可以在ts docs中找到更多解释。

虽然此标志不会影响您的示例的行为 - 它为您提供了对您遇到的错误的很好的官方解释。

如果你想让它工作,你只需要做Test2部分:

type Test2 = { [P in Keys]?: Props[P] } // <---- added question mark

或者:

type Test2 = Partial<{ [P in Keys]: Props[P] }>

为什么keyof Props不一样Keys

我在回答这个问题时没有考虑到这种行为。

似乎是 in-Test1不会keyof Props为 Props 键创建单独的中间联合类型,并直接通过Props接口键进行迭代,同时考虑到所有修饰符,而在迭代过程中Test2,TS 遍历 chars: 的联合,a | b | c | d并且对Props.

我在使用枚举时遇到了类似的行为:


enum MyEnum {
    ONE,
    TWO
}

type Enumerate<Enum extends number | string> = keyof {
    [Prop in Enum]: Prop
}

type Result2 = Enumerate<MyEnum>

Enumerate应该返回MyEnum与其联合ONE | TWO但它返回的键MyEnum

于 2021-10-11T09:39:30.043 回答
0

这是来源Omit

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

所以这就是这里的区别——<code>keyof 获取所有键并将它们表示为非可选并构造一个对象,而Pick从原始对象构造对象,并保留T可选声明。

于 2021-10-11T09:35:46.247 回答