在TS越来越流行的趋势下,工程使用TS已经成为常态化了,你是否还是一个any走天下呢? 本文主要是一些常用的TS工具类的实现,同时以JS的方式类比实现,帮助更好的理解TS工具类的使用。
首先介绍一些基础的类型
keyof
keyof运算符接受一个对象类型,并生成其键的字符串或数字文字并集
「事例」
type Point = {
x:number
y:number
}
type TP = keyof Point
// type TP = 'x'|'y'
const CP:TP = 'x'
typeof
获取变量或者对象属性的类型 「分析」
- 类似JavaScript中typeof,返回其类型
「事例」
const person = {
name:'zhangsan',
age:12,
sex:'男'
}
type TPerson = typeof person
// type TPerson = {
// name: string;
// age: number;
// sex: string;
// }
infer 关键字
动态推断当前的类型
「事例」
interface base {
d:{
d1:string
d2:number
d3?:boolean
}[]
}
type getBaseKeyType<T,K extends keyof T> = T[K] extends (infer U)[] ? U:T[K]
type b1 = getBaseKeyType<base,'d'>
// b1当前的类型
// type b1 = {
// d1: string;
// d2: number;
// d3?: boolean | undefined;
// }
下面是一些日常使用的TS工具类函数
首先是类型的基本定义 然后是分析工具类实现的实质 其后是JS方式类比实现 最后是TS工具的具体使用事例
Partial
构造一个类型,该类型的所有属性都设置为可选。此实用程序将返回一个表示给定类型的所有子集的类型。
「分析」
- 使用属性集合是输入属性集合的子集
- 不允许出现非输入属性集合的属性
- 返回使用属性集合
TS 版实现
type Partial<T> = {
[P in keyof T]?: T[P];
};
// keyof T 返回类型属性key集合,类似JS中Object.keys(T)
// in 循环遍历
JS 版类比
// K表示使用时的类型
function Partial(T, K) {
const tmp = {};
for (let key in K) {
if (key in T) {
tmp[key] = T[key];
} else {
return new Error(`“${key}”不在“${T}”中`);
}
}
return tmp;
}
「事例」
interface IPerson {
name:string
age:number
}
type p = Partial<IPerson>
// p的类型
// type p = {
// name?: string | undefined;
// age?: number | undefined;
// }
const p1:Partial<IPerson> = { name:'zhangsan'}
Required
构造一个类型,该类型由设置为required的所有属性组成。与Partial相反
「分析」
- 使用属性集合和输入属性集合相同
- 不允许出现非输入属性集合的属性
- 返回使用属性集合
TS 版实现
type Required<T> = {
[P in keyof T]-?: T[P];
};
// keyof T 返回类型属性key集合,类似JS中Object.keys(T)
// in 循环遍历
// -? 去掉可选属性标识
JS 版类比
// K表示使用时的类型
function Required(T, K) {
const tmp = {};
for (let key in T) {
if (key in K) {
// 如果T[key]存在
tmp[key] = T[key];
} else {
// 缺少属性
return new Error("缺少key属性");
}
}
return tmp;
}
「事例」
interface IPerson {
name:string
age?:number
}
type P = Required<IPerson>
// P的类型
// type P = {
// name: string;
// age: number;
// }
const P1:Required<IPerson> = {name:'zhangsan',age:12}
Readonly
构造一个类型,该类型的所有属性都设置为只读,这意味着无法重新分配构造类型的属性。
「分析」
- 存在的属性只读
- 不允许新增属性
TS 版实现
type Readonly<T> = { readonly [P in keyof T]: T[P]; }
JS 版类比
function Readonly(T) {
return Object.freeze(T);
}
「事例」
interface IPerson {
name:string
age?:number
}
type P = Readonly<IPerson>
// type P = {
// readonly name: string;
// readonly age?: number | undefined;
// }
const P1:Readonly<IPerson> = {name:'zhangsan'}
P1.name // zhangsan
// P1.name = 'lisi' // Error 无法分配到 "name" ,因为它是只读属性
Record<Keys, Type>
构造一个对象类型,其属性键为键,属性值为类型。此实用程序可用于将一个类型的属性映射到另一个类型。
「分析」
- 返回对象类型
- 全部 key 的类型转换输入类型
TS 版实现
type Record<K extends string | number | symbol, T> = { [P in K]: T; }
JS 版类比
function Record(K, T) {
const tmp = {};
for (let key of K) {
tmp[key] = T;
}
return tmp;
}
「事例」
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
type CN = Record<CatName, CatInfo>;
// type CN = {
// miffy: CatInfo;
// boris: CatInfo;
// mordred: CatInfo;
// }
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" },
};
Exclude<UnionType, ExcludedMembers>
通过从UnionType中排除可分配给ExcludedMembers的所有联合成员来构造类型
「分析」
- 返回输入联合类型(UnionType)非交集部分
- 允许不包含属性中出现不存在的属性
TS 版实现
type Exclude<T, U> = T extends U ? never : T
JS 版类比
function Exclude(T,U){
return T.filter(key => !U.includes(key))
}
「事例」
interface IPerson {
name: string;
age?: number;
}
type e = Exclude<keyof IPerson,'sex'>
// type e = "age" | "name"
const Pe:Exclude<keyof IPerson,'sex'> = 'age'
type T0 = Exclude<"a" | "b" | "c", "a">;
// type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// type T1 = "c"
type T2 = Exclude<string | number | (() => void), Function>;
// type T2 = string | number
Extract<Type, Union>
通过从类型中提取联合类型的交集
「分析」
- 取两个类型的交集
TS 版实现
type Exclude<T, U> = T extends U ? T : never
JS 版类比
function extract(T,U){
return T.filter(key => U.includes(key))
}
「事例」
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
// type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;
// type T1 = () => void
Pick<Type, Keys>
通过从类型中选取属性键集(字符串文本或字符串文本的并集)来构造类型。
「分析」
- 键集合是类型属性集合的子集
- 不允许出现非输入属性集合的属性
- 返回键属性的集合
TS 版实现
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }
JS 版类比
function Pick(T,K){
const tmp = {}
for(let key of K){
if(key in T){
tmp[key] = T[key]
} else {
return new Error(`${key} 不在${T}属性中`)
}
}
return tmp
}
「事例」
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
// type TodoPreview = {
// title: string;
// completed: boolean;
// }
const todo: Pick<Todo, "title" | "completed"> = {
title: "Clean room",
completed: false,
};
Omit<Type, Keys>
通过从类型中选取所有属性,然后移除键(字符串文字或字符串文字的并集)来构造类型。
「分析」
- 删除类型属性中的键属性集合
- 返回类型属性删除后键属性集
- 允许出现类型属性中不存在的键
TS 版实现
type Omit<T, K extends keyof any> = { [P in Exclude<keyof T, K>]: T[P]; }
JS 版类比
function omit(T,K){
const tmp = {...T}
for(let key of K){
if(tmp[key]){
delete tmp[key]
}
}
return tmp
}
「事例」
interface IPerson {
name: string;
age?: number;
}
type o1 = Omit<IPerson,'age'|'sex'>
// type o1 = {
// name: string;
// }
const o2:Omit<IPerson,'age'|'sex'> = {name:'zhangsan'}
NonNullable
构造一个不包含null和undefined的类型 「分析」
- 去取null和undefined
TS 版实现
type NonNullable<T> = T extends null | undefined ? never : T
JS 版类比
function nonNullable(T){
return T.filter(val => (val !== null && val!== undefined ))
}
「事例」
type T0 = NonNullable<string | number | undefined>;
// type T0 = string | number
type T1 = NonNullable<string[] | null | undefined>;
// type T1 = string[]
Parameters
从函数类型的参数中使用的类型构造元组类型。
「分析」
- 获取参数的类型,构成元组类型
TS 版实现
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
JS 版类比
let name = 'zhangsan'
let age = 22
function parameters(fn) {
fn(name,age)
return [name,age]
}
parameters((name, age) => console.log(name, age));
「事例」
type fn = (name:string,age:number) => void
type T1 = Parameters<fn>
// type T1 = [name: string, age: number]
type T2 = Parameters<() => void>
// type T2 = []
type T3 = Parameters<<T>(args:T) => T>
// type T3 = [args: unknown]
ReturnType
构造一个由函数类型的返回类型组成的类型。 「分析」
- 获取函数的返回类型
TS 版实现
type ReturnType<T extends (...args:any) => any> = T extends (...args:any) => (infer U )? U : never
JS 版类比
function returnType(fn){
const res = fn()
return res
}
returnType(() => console.log('test'))
「事例」
type TR1 = ReturnType<() => void>
// type TR1 = void
type TR2 = ReturnType<any>
// type TR2 = any
type TR3 = ReturnType<<T>() => T>
// type TR3 = unknown
type TR4 = ReturnType<() => {name:string}>
// type TR4 = {
// name: string;
// }
InstanceType
构造由类型中构造函数的实例类型组成的类型。 「分析」
- 获取类的类型,约束创建类的实例,类似instanceof
TS 版实现
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any
JS 版类比
class A {
x = 0
y = 0
say(){
}
}
const AA = new A()
console.log(AA instanceof A);
「事例」
class A {
x = 0
y = 0
say(){
}
}
const AA = new A()
console.log(AA instanceof A);
class B extends A {}
const C:InstanceType<typeof A> = new B()
// InstanceType<typeof A> 返回 A 的约束类型
「总结:」
上面的都掌握了,应对日常的开发类型定义基本上已经满足,希望在舍弃any的路上越走越远。