typescript 如何使一个属性在一个类型脚本类型中成为非可选的?

xkftehaa  于 2023-03-04  发布在  TypeScript
关注(0)|答案(8)|浏览(163)

我有这种类型:

type User = { 
  id: string;
  name?: string;
  email?: string;
}

我想用name non optional构造一个类似的类型:

type UserWithName = {
  id: string;
  name: string;
  email?: string;
}

与其像上面那样重复类型,我如何使用泛型实用程序类型从User构造UserWithName呢?
Required几乎完成了这项工作,但它将 * 所有 * 属性设置为非可选,而我只想设置 * 一个 * 属性。

cuxqih21

cuxqih211#

如果你需要使嵌套属性成为强制性的,那么交集就是你的朋友。它既适用于接口也适用于类型。

// This can be a type too
interface User {
  id: string
  properties: {
    name?: string
    email?: string
  }
}

// Make properties.name mandatory
type UserWithEmail = User & { properties: { name: string } }
idfiyjo8

idfiyjo82#

您可以在type-fest中使用SetRequired<Obj, 'key1' | 'key2'>作为implemented here

yvfmudvl

yvfmudvl3#

如果您不想创建帮助类型,可以将必需与拾取结合使用

type User = { 
  id: string;
  name?: string;
  email?: string;
}

const user: Required<Pick<User, 'name'>> & User = {
  id: '1',
  name: 'Jim'
}
mznpcxlj

mznpcxlj4#

如果您检查Required类型的源代码,它是:

type Required<T> = {
  [P in keyof T]-?: T[P]
}

同样的语法也可以用来构造一个泛型类型,它将给予您想要的结果:

type User = {
  id: string
  name?: string
  email?: string
}

type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }

type UserWithName = WithRequired<User, 'name'>

// error: missing name
const user: UserWithName = {
  id: '12345',
}

Playground链接

y4ekin9u

y4ekin9u5#

您可以改用接口:

interface User  { 
  id: string;
  name?: string;
  email?: string;
}

interface UserWithName extends User {
  name: string;
}

现在你已经构建了User类型,但是重写了name属性使其成为强制性的。看看这个类型脚本练习场-没有name属性的UserWithName将出错。

vshtjzan

vshtjzan6#

我认为这更简单,也很好用。给定User类型,你想让name属性成为非可选的:

type User = { 
  id: string;
  name?: string;
  email?: string;
}

要创建命名接口:

interface UserWithName extends User {
  name: NonNullable<User['name']>
}
let personWithName1: UserWithName;

创建命名类型:

type UserWithName = User & {
  name: NonNullable<User['name']>
}
let personWithName2: UserWithName;

创建匿名类型:

let personWithName3: User & { name: NonNullable<User['name']> }

我喜欢这种方法,因为您不需要定义一个新的泛型,但是您仍然可以从能够在User中自由更改name属性的类型中获益,并且如果您更改name属性的名称本身,也会显示一个错误。
如果你不完全明白发生了什么,我用:

  • 内置的NonNullable实用工具类型。
  • 用于获取属性类型的查找类型。
  • 用于扩展类型的交集类型(extends关键字的替代方法,该关键字仅适用于命名接口)。
nlejzf6q

nlejzf6q7#

我改进了上级提出的建议。

type Required<T, K extends keyof T> = T & { [P in K]-?: T[P] }

type WithRequired<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> & Required<T, K>

现在,您可以同时要求多个属性

interface foo {x?: number, y?: String}

WithRequired<foo, 'x' | 'y'>

现在需要所有属性

vnzz0bqm

vnzz0bqm8#

对于那些寻找使用实用程序类型的答案的人来说,这应该是可行的

type WithRequiredProp<Type, Key extends keyof Type> = Type & Required<Pick<Type, Key>>;

相关问题