一看就会的TS工具类

823 阅读6分钟

在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的路上越走越远。