实现Pick
- 不使用 Pick<T, K> ,实现 TS 内置的 Pick<T, K> 的功能。
- 从类型 T 中选出符合 K 的属性,构造一个新的类型。
interface Todo {
title: string
description: string
completed: string
}
type MyPick<T, K extends keyof T> = {
[key in K]: T[key]
}
type TodoPreview = MyPick<Todo, 'title' | 'description'>
const todo: TodoPreview = {
title: '',
description: ''
}
关键点:
- K extends keyof T :判断K必须是的T内的类型属性
- [key in K]: T[key] :遍历K,然后赋值,因为K是T类型的属性组,所以使用T[key]作为类型赋值
对象属性只读
- 不要使用内置的Readonly
,自己实现一个。 - 泛型 Readonly
会接收一个 泛型参数,并返回一个完全一样的类型,只是所有属性都会是只读 (readonly) 的。 - 也就是不可以再对该对象的属性赋值。
type MyReadonly<T> = {
readonly [P in keyof T]: T[P]
}
type Person = {
name:string
sex: string
}
type ReadOnlyPerson = MyReadonly<Person>
// type ReadOnlyPerson = {
// readonly name: string;
// readonly sex: string;
// }
关键点:
- readonly的使用
- [P in keyof T] :T[P] 遍历T泛型的类型属性,然后进行赋值,这与之前使用的key in K有所区别
第一个元素
type First<T extends any[]> = T extends [] ? never : T[0]
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // 'a'
type head2 = First<arr2> // 3
关键点:
- T extends any[] :代表泛指所有的类型数组是T
- T extends [] ? never : T[0] :此时的[]不是代表所有类型的数组而是代表空数组,整体意思就是T如果是空数组则返回never否则返回第一个类型
- never :代指不存在的结果,因为如果是空数组的话那么就没有第一个元素值
获取元组长度
- 创建一个Length泛型,这个泛型接受一个只读的元组,返回这个元组的长度。
type Length<T extends readonly any[]> = T['length']
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type teslaLength = Length<tesla> // expected 4
关键点:
- T['length'] :返回类型数组的长度
元组转换为对象
- 将一个元组类型转换为对象类型,这个对象类型的键/值和元组中的元素对应。`
type TupleToObject<T extends readonly (symbol | number | string)[]> = {
[P in T[number]]: P
}
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] :元组 T 中所有可能的元素类型的联合类型:此例子中就是symbol | number | string
- extends readonly : 代表必须是只读属性
- (symbol | number | string)[] :代表symbol | number | string 三种数组
- T extends readonly (symbol | number | string)[] :整体意思就是T泛型必须是可读的并且是symbol[] 或 string[] 或 string[]
实现 Exclude
- 实现内置的 Exclude<T, U> 类型,但不能直接使用它本身。
type MyExclude<T, U> = T extends U ? never : T
type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
关键点:
- T extends U的执行逻辑:T extends U: 当 T 是一个联合类型时,例如 T = A | B | C,T extends U 不会被当作一个整体的检查。 而是会分别检查联合类型 T 的每个成员:A extends U 吗?B extends U 吗?C extends U 吗?
Awaited
假如我们有一个 Promise 对象,这个 Promise 对象会返回一个类型。在 TS 中,我们用 Promise 中的 T 来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。
type MyAwaited<T> =
T extends PromiseLike<infer Val>
? MyAwaited<Val>
: T
type Z = Promise<Promise<string | number>>
type test = MyAwaited<Z> // string | number
关键点:
- PromiseLike
- PromiseLike 是 TypeScript 内置的一个接口,用来描述类似 Promise 的对象。它不一定是真正的 Promise,但必须具有 then 方法。
- infer Val: 这是类型推断 (Type Inference) 的关键字。 infer Val 的意思是:如果 T 能够赋值给 PromiseLike<...> 类型,那么 TypeScript 会尝试推断 PromiseLike 的类型参数,并将推断出的类型赋值给类型变量 Val。 换句话说,Val 代表了 PromiseLike 最终 resolve 的值的类型。
If
- 实现一个 IF 类型,它接收一个条件类型 C ,一个判断为真时的返回类型 T ,以及一个判断为假时的返回类型 F。 C 只能是 true 或者 false, T 和 F 可以是任意类型。
type If<C extends boolean, T, F> = C extends true ? T : F
关键点:
- C extends true 类型 true 与布尔值 true 的区别:
- 类型 true: 这是一个类型,它只包含一个值,就是 JavaScript 的布尔值 true。
- 布尔值 true: 这是一个 JavaScript 的运行时值。
- 在类型定义中,我们操作的是类型,所以需要使用类型层面的操作符,即 extends
Concat
- 在类型系统里实现 JavaScript 内置的 Array.concat 方法,这个类型接受两个参数,返回的新数组类型应该按照输入参数从左到右的顺序合并为一个新的数组。
type Concat<T extends readonly unknown[], U extends readonly unknown[]> = [...T, ...U]
关键点:
- [...T, ...U]: 这部分就是使用展开语法来构建新的元组类型。
Includes
在类型系统里实现 JavaScript 的 Array.includes 方法,这个类型接受两个参数,返回的类型要么是 true 要么是 false。
type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Last] ?
Equal<U, First> extends true ?
true :
Includes<Last, U> :
false
关键点:
- infer的使用:Awaited 已经介绍
- Equal<U, First>:Equal<A, B> 的类型工具,它的作用是判断类型 A 和类型 B 是否完全相等
Push
- 在类型系统里实现通用的 Array.push 。
// 根据之前联系过的知识可以实现
type Push<T extends unknown[], U> = [...T, U]
Unshift
- 实现类型版本的 Array.unshift。
// 与push类似
type Unshift<T extends unknown[], U> = [U, ...T]
Parameters
实现内置的 Parameters 类型,而不是直接使用它
- const foo = (arg1: string, arg2: number): void => {}
- type FunctionParamsType = MyParameters
// [arg1: string, arg2: number]
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer S) => any ? S : any