首页 > 解决方案 > 我可以定义一个模型,但需要一些字段的变化吗?

问题描述

对于我们的 API,您可以创建一个订单。该订单对于各种端点具有完全相同的模型,但变化的是所需的字段。

POST - 一些字段是必需的

PATCH - 所有字段都是可选的

PUT - 所有字段都是必需的

我可以在打字稿中对此进行建模,以便我只需要维护一个模型,然后提供有关 API 调用的一些信息来表示所需的字段吗?

标签: typescript

解决方案


您可以在 make-required 或 make-optional 方向上专门使用映射类型执行此操作(感谢 TS2.8 中添加的用于删除映射类型中的修饰符的支持)。在不具体了解您要操作的类型或键的情况下,我会这样做:

// Require<T, K> takes an object type T and a set of its keys K
// and produces a new type identical to T except that the properties at keys K
// are now required.  If K is left out, Require<T> behaves like Required<T>.
type Require<T, K extends keyof T = keyof T> = Pick<T, Exclude<keyof T, K>> &
  Required<Pick<T, K>> extends infer U
  ? { [P in keyof U]: U[P] }
  : never;

// Optionalize<T, K> takes an object type T and a set of its keys K
// and produces a new type identical to T except that the properties at keys K
// are now optional.  If K is left out, Optionalize<T> behaves like Partial<T>.
type Optionalize<T, K extends keyof T = keyof T> = Pick<T, Exclude<keyof T, K>> &
  Partial<Pick<T, K>> extends infer U
  ? { [P in keyof U]: U[P] }
  : never;

这些基本上是彼此的镜像......它们使用一堆内置的映射类型,如PickPartialRequired,以及一些自定义和内置的条件类型,如Exclude. 这个想法是您将属性拆分T为具有键的属性K和没有键的属性。您修改 中的那些K,不理会其他的,然后构建一个将所有属性重新组合在一起的新类型。让我们测试一下:

// Tests
interface AnInterface {
  req1: string;
  readonly req2: number;
  readonly opt1?: boolean;
  opt2?: object;
}

type SomeRequired = Require<AnInterface, "req1" | "opt1">;
// type SomeRequired = {
//   readonly req2: number;
//   opt2?: object | undefined;
//   req1: string; // was already required, still is
//   readonly opt1: boolean; // now required
// }

type SomeOptional = Optionalize<AnInterface, "req1" | "opt1">;
// type SomeOptional = {
//   readonly req2: number;
//   opt2?: object | undefined;
//   req1?: string | undefined; // now optional
//   readonly opt1?: boolean | undefined; // was already optional, still is
// }

那些对我来说看起来不错。当然,如果您不需要像这样的通用解决方案,您可以使用您的特定对象类型和键名执行相同或相似的类型映射。由你决定。

好的,希望有帮助;祝你好运!

链接到代码


推荐阅读