type-challenges:RequiredByKeys

27 阅读2分钟

RequiredByKeys

问题描述

实现一个通用的RequiredByKeys<T, K>,它接收两个类型参数TK

K指定应设为必选的T的属性集。当没有提供K时,它就和普通的Required<T>一样使所有的属性成为必选的。

例如:

interface User {
  name?: string
  age?: number
  address?: string
}
​
type UserRequiredName = RequiredByKeys<User, 'name'> // { name: string; age?: number; address?: string }
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'interface User {
  name?: string
  age?: number
  address?: string
}
​
interface UserRequiredName {
  name: string
  age?: number
  address?: string
}
​
interface UserRequiredNameAndAge {
  name: string
  age: number
  address?: string
}
​
type cases = [
  Expect<Equal<RequiredByKeys<User, 'name'>, UserRequiredName>>,
  Expect<Equal<RequiredByKeys<User, 'name' | 'age'>, UserRequiredNameAndAge>>,
  Expect<Equal<RequiredByKeys<User>, Required<User>>>,
  // @ts-expect-error
  Expect<Equal<RequiredByKeys<User, 'name' | 'unknown'>, UserRequiredName>>
]
​
type Compute<T extends object> = { [K in keyof T]: T[K] }
// ============= Your Code Here =============
// 答案1
type RequiredByKeys<T extends object, K extends keyof T = keyof T> = Compute<Omit<T, K> & Required<Pick<T, K>>>
// 答案2
type RequiredByKeys<T, K extends keyof T = keyof T> = Omit<T & Required<Pick<T, K & keyof T>>, never>;

思路其实很简单,和上一题刚好相反,只需要将第二个泛型参数从类型中剔除出来并将其变成必选属性,使用联合类型传入 Compute 类型中映射为一个新的类型即可。

答案1

这段代码定义了一个名为RequiredByKeys的类型操作符,它接受两个类型参数TKT表示对象类型,K表示键类型。RequiredByKeys的实现原理是使用Compute类型操作符和OmitPick两个类型操作符。

  1. Compute<Omit<T, K> & Required<Pick<T, K>>>:这是RequiredByKeys的实现。Omit<T, K>表示从类型T中移除键KRequired<Pick<T, K>>表示从类型T中选择键K,但确保它们在对象中存在。
  2. T extends object:表示T必须是一个对象类型。
  3. K extends keyof T = keyof T:表示K必须是一个键类型,并且默认情况下等于T的键类型。

答案2

这段代码定义了一个名为RequiredByKeys的类型操作符,它接受两个类型参数TKT是原始对象类型,K是键的类型,默认为keyof T

实现原理:

  1. 使用Required<Pick<T, K & keyof T>>创建一个新对象,其中K & keyof T是对象的键,Pick<T, K & keyof T>是从原始对象中选择这些键的子集。Required<T>会确保对象中的所有属性都是必需的(即不会为undefinednull)。
  2. 使用Omit<T & Required<Pick<T, K & keyof T>>, never>创建一个新对象,其中T & Required<Pick<T, K & keyof T>>是必需的原始对象,Omit<T & Required<Pick<T, K & keyof T>>, never>会从必需的原始对象中移除所有键为never的属性。