ts类型挑战【二十二】

244 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

题目三十七:pickbytype

// template.ts
type PickByType<T, U> = any
// test-case.ts
import type { Equal, Expect } from '@type-challenges/utils'

interface Model {
  name: string
  count: number
  isReadonly: boolean
  isEnable: boolean
}

type cases = [
  Expect<Equal<PickByType<Model, boolean>, { isReadonly: boolean; isEnable: boolean }>>,
  Expect<Equal<PickByType<Model, string>, { name: string }>>,
  Expect<Equal<PickByType<Model, number>, { count: number }>>,
]

从“T”中,选择一组类型可分配给“U”的属性。

测试用例

  • Equal<PickByType<Model, boolean>, { isReadonly: boolean; isEnable: boolean }>

Model 中拿出值为 boolean 的内容,期望结果为 :{ isReadonly: boolean; isEnable: boolean }

  • Equal<PickByType<Model, string>, { name: string }>

Model 中拿出值为 string 的内容,期望结果为 :{ name: string }

  • Equal<PickByType<Model, number>, { count: number }>

Model 中拿出值为 number 的内容,期望结果为 :{ count: number }

代码实现

  • 原代码
type PickByType<T, U> = any
  • 因为我们需要从 T 中获取特定的内容,那么我们先对 T 进行遍历
type PickByType<T, U> = {
  [P in keyof T]: T[P]
}
  • 遍历过程中,需要去除一些不必要的项,只留下存在于 U 中相对应的项

做法:将 keyof T 进行转换 (as

看看值(T[P])是否在 U 中,如果存在则正常返回 P,不存在则返回 never(返回 never 的话,当前循环项就不会出现在结果中了)

type PickByType<T, U> = {
  [P in keyof T as T[P] extends U ? P : never]: T[P]
}

题目三十八:startswith

// template.ts
type StartsWith<T extends string, U extends string> = any
// test-cases.ts
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<StartsWith<'abc', 'ac'>, false>>,
  Expect<Equal<StartsWith<'abc', 'ab'>, true>>,
  Expect<Equal<StartsWith<'abc', 'a'>, true>>,
  Expect<Equal<StartsWith<'abc', 'abcd'>, false>>,
]

实现 'StartsWith<T,U>',它接受两种精确的字符串类型,并返回'T'是否以'U'开头

代码实现

  • 原代码
type StartsWith<T extends string, U extends string> = any
  • 直接对 T 进行拆分,但是拆分内容中包含 U
type StartsWith<T extends string, U extends string> = T extends `${U}${infer _}` ? true : false
  • 也可以写成
type StartsWith<T extends string, U extends string> = T extends `${U}${string}` ? true : false

题目三十九:partialbykeys

// template.ts
type PartialByKeys<T, K> = any
// test-cases.ts
import type { Equal, Expect } from '@type-challenges/utils'

interface User {
  name: string
  age: number
  address: string
}

interface UserPartialName {
  name?: string
  age: number
  address: string
}

interface UserPartialNameAndAge {
  name?: string
  age?: number
  address: string
}

type cases = [
  Expect<Equal<PartialByKeys<User, 'name'>, UserPartialName>>,
  Expect<Equal<PartialByKeys<User, 'name' | 'unknown'>, UserPartialName>>,
  Expect<Equal<PartialByKeys<User, 'name' | 'age'>, UserPartialNameAndAge>>,
  Expect<Equal<PartialByKeys<User>, Partial<User>>>,
]

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

K指定应设置为可选的T属性集。当没有提供 K 时,它应该使所有属性都是可选的,就像正常的 Partial<T>

测试用例

interface User {
  name: string
  age: number
  address: string
}
  • PartialByKeys<User, 'name'>

User 中的 name 属性变为可选,期望结果:{ name?: string, age: number, address: string }

  • PartialByKeys<User, 'name' | 'unknown'>

联合类型中的 unknown 不会影响结果

  • PartialByKeys<User>

如果不传入第二个参数,则全部变为可选

代码实现

  • 原代码
type PartialByKeys<T, K> = any
  • 使用 Omit 拿到 K 之外的内容。使用 K & keyof T 去除 T 之外的内容,例如:unknown
type PartialByKeys<T, K> = Omit<T, K & keyof T>
  • 合并剩余的 K 中的内容,并加上可选符号 ?
type PartialByKeys<T, K> = Omit<T, K & keyof T> & {
  [P in K & keyof T]?: T[P]
}
  • 此时还是报错状态,还需要对齐遍历(Copy)一份,因为 Omit 合并的缘故
type Copy<T> = {
  [P in keyof T]: T[P]
}
type PartialByKeys<T, K = keyof T> = Copy<Omit<T, K & keyof T> & {
  [P in K & keyof T]?: T[P]
}>