3 min read
type-challenges 简单难度

实现 Pick

不使用 Pick<T, K> ,实现 TS 内置的 Pick<T, K> 的功能。

从类型 T 中选出符合 K 的属性,构造一个新的类型。

例如:


interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
  title: 'Clean room',
  completed: false,
}

实现:


type MyPick<T extends Object, K extends keyof T> = { [key in K]: T[key] }

对象属性只读

不要使用内置的 Readonly<T>,自己实现一个。

泛型 Readonly<T> 会接收一个 泛型参数,并返回一个完全一样的类型,只是所有属性都会是只读 (readonly) 的。

也就是不可以再对该对象的属性赋值。

例如:


interface Todo {
  title: string
  description: string
}

const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar"
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property

实现:

使用 key in keyof T 提取 T 的键并遍历,key 代表 T 的每一个键


type MyReadonly<T> = { readonly [key in keyof T]: T[key] }

元组转换为对象

将一个元组类型转换为对象类型,这个对象类型的键/值和元组中的元素对应。

例如:


const tuple = [ 'tesla', 'model 3', 'model X', 'model Y' ] as const

type result = TupleToObject<typeof tuple> // expected { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

实现:

使用 T[number] 遍历数组作为 key


type TupleToObject<T extends readonly ( string | number | symbol )[]> = { [key in T[number]]: key } 

第一个元素

实现一个 First<T> 泛型,它接受一个数组T并返回它的第一个元素的类型。

例如:


type arr1 = [ 'a', 'b', 'c' ]
type arr2 = [ 3, 2, 1 ]

type head1 = First<arr1> // 应推导出 'a'
type head2 = First<arr2> // 应推导出 3

实现:


// 方法一:使用 infer 和 ... 提取出第一个元素和剩余元素

type First<T extends any[]> = T extends [ infer F, ...infer rest ] ? F : never

// 方法二: 使用 extends 判断 T 是否为空数组,如果不是返回 T[0]

type First<T extends any[]> = T extends [] ? never : T[0]

// 方法三: 使用 extends 判断 T['length'] 是否为 0,如果不是返回 T[0]

type First<T extends any[]> = T['length'] extends 0 ? never : T[0]

获取数组长度

创建一个 Length 泛型,这个泛型接受一个只读的元组,返回这个元组的长度。

例如:


type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']

type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5

实现:


type Length<T extends readonly any[]> = T['length']

实现 Exclude

实现内置的 Exclude<T, U> 类型,但不能直接使用它本身。

从联合类型 T 中排除 U 中的类型,来构造一个新的类型。

例如:


type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'

实现:

对于联合类型 T, T extends U 会检查 T 的每一个成员是否可以赋值给 U


type MyExclude<T,U> = T extends U ? never : T