Typescript类型体操(一)

201 阅读3分钟

一、实现Pick

答案:

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

相关概念

keyof

取interface的键后保存为联合类型

interface userInfo {
  name: string
  age: number
}
type keyofValue = keyof userInfo
// keyofValue = "name" | "age"

in

取联合类型的值,主要用于数组和对象的构建 注意不能用于interface中

type name = 'firstname' | 'lastname'
type TName = {
  [key in name]: string
}
// TName = { firstname: string, lastname: string }

实际用途

举个栗子

function getValue(o:object, key: string){
  return o[key]
}
const obj1 = { name: '张三', age: 18 }
const values = getValue(obj1, 'name')

上述代码虽然可以运行,但是失去了typescript本身的优势:

  1. 无法确定返回值类型
  2. 无法对key进行约束
function getValue<T extends Object,K extends keyof T>(o: T,key: K): T[K] {
  return o[key]
}
const obj1 = { name: '张三'age: 18}
const values = getValue(obj1, 'name')
// 如果第二个参数不是obj1中的参数就会报错

二、实现ReadOnly

答案

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

相关概念

readonly修饰符,首先是一个关键字,对类中的属性成员进行修饰,修饰之后,该属性成员就不能修改了,只能够进行访问。

三、元组转对象

传入一个元组类型,将这个元组类型转换为对象类型,这个对象类型的键/值都是从元组中遍历出来。

答案

type TupleToObject<T extends readonly any[]> = {
  [k in T[number]]: k
}

第一个元素

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

答案(版本一)

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

使用类型推断获取到第一个元素类型并返回

答案(版本二)

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

获取元组长度

创建一个通用的Length,接受一个readonly的数组,返回这个数组的长度。

答案:

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

Exclude

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

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

答案

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

有关extends用法:TS关键字extends用法总结

Awaited

假如我们有一个 Promise 对象,这个 Promise 对象会返回一个类型。在 TS 中,我们用Promise<T> 中的T来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。

例如:Promise<ExampleType>,请你返回 ExampleType 类型。

答案

type MyAwaited<T> = T extends Promise<infer R> ? R extends Promise<unknown>? MyAwaited<R> : R : never

有关infer的用法:精读《Typescript infer 关键字》 - 掘金 (juejin.cn)

IF

实现一个 IF 类型,它接收一个条件类型 C ,一个判断为真时的返回类型 T ,以及一个判断为假时的返回类型 FC 只能是 true 或者 falseTF 可以是任意类型。

答案

type If<C, T, F> = C extends true ? T : C extends false ? F : never

concat

在类型系统里实现 JavaScript 内置的 Array.concat 方法,这个类型接受两个参数,返回的新数组类型应该按照输入参数从左到右的顺序合并为一个新的数组。

例如:

type Result = Concat<[1], [2]> // expected to be [1, 2]

答案:

type Concat<T extends any[], U extends any[]> = [...T, ...U]

Includes

在类型系统里实现 JavaScript 的 Array.includes 方法,这个类型接受两个参数,返回的类型要么是 true 要么是 false

例如:

type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`

答案

type Equal<X, Y> = (<T>() => T extends X ? 1: 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;

type Includes<T extends readonly any[], U> = T extends [infer K, ...infer R] ? 
                                                Equal<U, K> extends true ? 
                                                  true
                                                  :
                                                  Includes<R, U>
                                                : false

这个题有一种简单的答案:

type Includes<T extends unknown[], K> = K extends T[keyof T] ? true : false;

但是如果去判断下面这种,就会出现错误:

<Includes<[boolean, 2, 3, 5, 6, 7], false> //expected to be `false` but `true`