在TypeScript中使单个属性可选

Dal*_*onF 14 typescript

在TypeScript中,2.2 ......

假设我有一个Person类型:

interface Person {
  name: string;
  hometown: string;
  nickname: string;
}
Run Code Online (Sandbox Code Playgroud)

我想创建一个返回Person的函数,但不需要昵称:

function makePerson(input: ???): Person {
  return {...input, nickname: input.nickname || input.name};
}
Run Code Online (Sandbox Code Playgroud)

应该是什么类型的input?我正在寻找一种动态方式来指定一个类型,Person除了nickname可选(nickname?: string | undefined)之外.到目前为止,我发现的最接近的是:

type MakePersonInput = Partial<Person> & {
  name: string;
  hometown: string;
}
Run Code Online (Sandbox Code Playgroud)

但这并不是我想要的,因为我必须指定所需的所有类型而不是可选的类型.

Tim*_*m K 40

这是我的 Typescript 3.5+ 可选实用程序类型

type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;

// and your use case
type MakePersonInput = Optional<Person, 'nickname'>

// and if you wanted to make the hometown optional as well
type MakePersonInput = Optional<Person, 'hometown' | 'nickname'>
Run Code Online (Sandbox Code Playgroud)

  • 这太美了! (8认同)
  • 当然!“Optional”有两个参数,“T”是我们想要作为可选类型基础的类型,“K”表示类型“T”上可用的一组键。`Partial&lt;T&gt;` 返回一个类型,其中 `T` 中的所有键都标记为可选。用 `Pick&lt;...,K&gt;` 包围 `Partial&lt;T&gt;` 为我们提供了一个仅包含我们提供的键的类型,我们已经将其设为可选。使用 Omit&lt;T,K&gt; 给我们一个没有任何我们指定的键的类型。通过使用“&amp;”,它将两种类型联合在一起。 (2认同)

小智 30

你也可以这样做,只部分键.

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>

interface Person {
  name: string;
  hometown: string;
  nickname: string;
}

type MakePersonInput = PartialBy<Person, 'nickname'>
Run Code Online (Sandbox Code Playgroud)

  • `Pick&lt;T,K&gt;` 实际上似乎没有必要:`Omit&lt;T, K&gt; &amp; Partial&lt;T&gt;` 也能完成工作。 (11认同)
  • 这太棒了!请注意,您可以传递多个属性:type OnlyNameIsMandatory = PartialBy&lt;Person, 'nickname'|'hometown'&gt; (6认同)
  • 快速说明,默认情况下,[TypeScript 3.5+](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/#the-omit-helper-type中提供了`Omit`帮助器类型的默认设置)。因此,如果您使用的是TS 3.5或更高版本,则不再需要自己定义“忽略”。 (6认同)
  • @ThanosM你可以使用`interface PersonExtended extends MakePersonInput { some_prop: string; }` 或交集类型 `type MakePersonInput = PartialBy&lt;Person, 'nickname'&gt; &amp; {some_prop: string}` (2认同)

Phi*_*ley 24

对于即插即用解决方案,请考虑使用出色的utility-types软件包:

npm i utility-types --save
Run Code Online (Sandbox Code Playgroud)

然后只需使用Optional<T, K>

import { Optional } from 'utility-types';

type Person = {
  name: string;
  hometown: string;
  nickname: string;
}

type PersonWithOptionalNickname = Optional<Person, 'nickname'>;

// Expect:
//
// type PersonWithOptionalNickname {
//   name: string;
//   hometown: string;
//   nickname?: string;
// }
Run Code Online (Sandbox Code Playgroud)

  • 我不知道这个包裹。太棒了,谢谢! (3认同)

Dal*_*onF 11

更新:

从TypeScript 2.8开始,条件类型更简洁地支持它!到目前为止,这似乎比以前的实现更可靠.

type Overwrite<T1, T2> = {
    [P in Exclude<keyof T1, keyof T2>]: T1[P]
} & T2;

interface Person {
  name: string;
  hometown: string;
  nickname: string;
}

type MakePersonInput = Overwrite<Person, {
  nickname?: string;
}>

function makePerson(input: MakePersonInput): Person {
  return {...input, nickname: input.nickname || input.name};
}
Run Code Online (Sandbox Code Playgroud)

和以前一样,MakePersonInput相当于:

type MakePersonInput = {
    name: string;
    hometown: string;
} & {
    nickname?: string;
}
Run Code Online (Sandbox Code Playgroud)

已过期:

从TypeScript 2.4.1开始,看起来还有另一种选择,如GitHub用户ahejlsberg在类型减法的线程中所提出的:https://github.com/Microsoft/TypeScript/issues/12215#issuecomment-307871458

type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
type Overwrite<T, U> = { [P in Diff<keyof T, keyof U>]: T[P] } & U;

interface Person {
  name: string;
  hometown: string;
  nickname: string;
}
type MakePersonInput = Overwrite<Person, {
  nickname?: string
}>
function makePerson(input: MakePersonInput): Person {
  return {...input, nickname: input.nickname || input.name};
}
Run Code Online (Sandbox Code Playgroud)

根据Intellisense,MakePersonInput相当于:

type MakePersonInput = {
    name: string;
    hometown: string;
} & {
    nickname?: string;
}
Run Code Online (Sandbox Code Playgroud)

看起来有点搞笑,但绝对可以完成工作.

在缺点方面,Diff在我开始了解它是如何工作之前,我需要盯着那种类型一段时间.

  • 使用它来保留可选字段 - 类型覆盖<T1,T2> =选择<T1,排除<keyof T1,keyof T2 >>&T2; (8认同)
  • 请注意,这使得原始界面中的任何可选字段都是必需的 (2认同)

Pet*_*ieg 11

type -fest包有一个实用程序SetOptional- https://github.com/sindresorhus/type-fest/blob/main/source/set-optional.d.ts

import { SetOptional } from 'type-fest';

type PersonWithNicknameOptional = SetOptional<Person, 'nickname'>
Run Code Online (Sandbox Code Playgroud)

我发现该库维护良好,并且支持最新版本的打字稿。在我看来,值得添加到打字稿项目中。


Ale*_*min 10

如果您使用最新版本的打字稿,一个简单的解决方案是

function makePerson(input: Omit<Person, 'nickname'> & { nickname?: string }): Person {
  return {...input, nickname: input.nickname || input.name};
}
Run Code Online (Sandbox Code Playgroud)

基本上,您从界面中删除“昵称”属性并将其重新添加为可选

如果你想确保它与原始界面保持同步,你可以这样做

Omit<Person, 'nickname'> & Partial<Pick<Person, 'nickname'>>
Run Code Online (Sandbox Code Playgroud)

如果您更改原始界面中的“昵称”属性,它会警告您