首页 > 解决方案 > 如何确保一个接口只包含另一个接口的属性?

问题描述

假设我有以下界面。

interface Client {
  id: number;
  email: string;
  firstName: string;
  lastName: string;
  cellNumberFull: string;
}

而且我希望以下界面仅包含GoClient.

interface ClientRestricted {
  firstName: string;
  lastName: string;
  cellNumberFull: string;
  foo: string; // This would throw an error
}

寻找一些与扩展相反的国王。这样的事情存在吗?

标签: typescript

解决方案


我倾向于让你的接口扩展一个映射的条件类型,它本身就是一个函数。这是一个递归类型定义(称为F-bounded quantification),可让您执行一些相当强大(如果令人困惑)的类型约束。例如:

type Restrict<T, U> = { [K in keyof U]: K extends keyof T ? T[K] : never };
type RestrictClient<U> = Restrict<Client, U>;

// okay as desired
interface Okay extends RestrictClient<Okay> {
  firstName: string;
  lastName: string;
  cellNumberFull: string;
}

// error, as desired
interface Extra extends RestrictClient<Extra> {
  //      ~~~~~
  // Types of property 'foo' are incompatible.
  // Type 'string' is not assignable to type 'never'.
  firstName: string;
  lastName: string;
  cellNumberFull: string;
  foo: string;
}

通过制作你的新界面I extends RestrictClient<I>,当且仅当Iis assignable to RestrictClient<I>,意思是 if Iis assignable to ,意思是{[K in keyof I]: K extends keyof Client ? Client[K] : never}如果每个 keyK存在并且是相同(或更窄)类型时,这将起作用。IClient

这也给出了以下行为,这可能会或可能不会解决您的用例:

// okay to narrow properties
interface Narrowed extends RestrictClient<Narrowed> {
  firstName: "specificString";
}

// error to widen properties
interface Widened extends RestrictClient<Widened> {
  //      ~~~~~~~ <-- number not assignable to string
  firstName: string | number;
}

// error to change property to unrelated types
interface Unrelated extends RestrictClient<Unrelated> {
  //      ~~~~~~~~~ <-- number not assignable to string
  firstName: number;
}

如果它与您要查找的内容不完全匹配,您可以更改 的定义Restrict以更紧密地对齐。无论如何,希望这能给你一些想法。祝你好运!

链接到代码


推荐阅读