Typescript是否支持"子集类型"?

Ric*_*ick 16 interface typescript

假设我有一个界面:

interface IUser {
  email: string;
  id: number;
  phone: string;
};
Run Code Online (Sandbox Code Playgroud)

然后我有一个期望该类型的子集(或完全匹配)的函数.也许它会通过一个完整的物体,使它只会传入{email: "t@g.com"}.我希望类型检查器允许两者.

例:

function updateUser(user: IUser) {
  // Update a "subset" of user attributes:
  $http.put("/users/update", user);
}
Run Code Online (Sandbox Code Playgroud)

Typescript是否支持这种行为呢?我觉得它非常有用,特别是像Redux这样的范例.

澄清一下,目标是:

  1. 避免重写接口并手动将所有属性设置为可选.
  2. 避免分配意外的属性(例如拼写错误).
  3. 避免使用命令逻辑,例如if语句,这会失去编译时类型检查的好处.

更新: Typescript宣布支持映射类型,一旦发布就应该解决这个问题.

Ric*_*ick 32

Typescript现在支持部分类型.

创建部分类型的正确方法是:

type PartialUser = Partial<IUser>;
Run Code Online (Sandbox Code Playgroud)

  • @James 请看看下面我的解决方案。这就是你所要求的。 (2认同)
  • @James`Pick`类型就是您所需要的。使用`Pick &lt;IUser,'id'| 'email'&gt;`(如果您根本不想允许电话),以及'Pick &lt;IUser,'id'| 'email'&gt;&Partial &lt;IUser&gt;`(如果要允许电话但不要求电话(同时需要ID和电子邮件))。有关详细信息,请参见我的答案。 (2认同)

Pat*_*ter 7

你想要的是这个

type Subset<T extends U, U> = U;
Run Code Online (Sandbox Code Playgroud)

这可以确保U是T的子集,并将U作为新类型返回。例如:

interface Foo {
 name: string;
 age: number;
}

type Bar = Subset<Foo, {
 name: string;
}>;
Run Code Online (Sandbox Code Playgroud)

you can not add new properties to Bar which are not part of Foo - and you can not alter types in a non-compatible way. this also works recursively on nested objects.


Dán*_*agy 7

值得注意的是Partial<T>,正如接受的答案中所建议的那样,使所有字段都是可选的,这不一定是您需要的。

如果您想将某些字段(例如idemail)设为必填字段,则需要将其与结合使用Pick

type UserWithOptionalPhone = Pick<IUser, 'id' | 'email'> & Partial<IUser>
Run Code Online (Sandbox Code Playgroud)

一些解释:

是什么Pick做的是,它可以让你指定接口的子集简洁(无需创建一个全新的界面重复字段类型,通过其他的答案的建议),然后让你使用那些和只有那些领域。

function hello1(user: Pick<IUser, 'id' | 'email'>) {
}

hello1({email: '@', id: 1}); //OK

hello1({email: '@'}); //Not OK, id missing

hello1({email: '@', id: 1, phone: '123'}); //Not OK, phone not allowed

Run Code Online (Sandbox Code Playgroud)

现在,这并不是我们想要的,我们想要允许,但不需要电话。为此,我们通过创建一个交集类型来 “合并”该类型的部分和“挑选”版本,该交集类型将具有idemail必填字段,而其他所有内容都是可选的-正是我们想要的。

function hello2(user: Pick<IUser, 'id' | 'email'> & Partial<IUser>) {
}

hello2({email: '@', id: 1}); //OK

hello2({email: '@', id: 1, phone: '123'}); //OK

hello2({email: '@'}); //Not OK, id missing
Run Code Online (Sandbox Code Playgroud)


Pen*_*zsó 6

具有映射类型的正确解决方案:

updateUser<K extends keyof IUser>(userData: {[P in K]: IUser[P]}) {
    ...
}
Run Code Online (Sandbox Code Playgroud)