likes
comments
collection
share

看懂ts类型体操之前,你需要先知道这些

作者站长头像
站长
· 阅读数 30

最近也是在开发ts项目,代码中有很多泛型嵌套,所以总结一下关于ts的简单类型操作。

泛型

  • 定义时不明确使用时必须明确成某种具体数据类型的数据类型。【泛型的宽泛】
  • 编译期间进行数据类型检查的数据类型。【泛型的严谨】

泛型约束

T extends object 表示T是一个对象类型,表示T是object的子类型。

const o: object = () => {} // object只能赋值对象类型,其中函数也是一个对象
type ObjTyp = { username: string, age: number }
type ObjectKeys<T extends object, K> = K extends keyof T ? K : never
type TestObjectKeys = ObjectKeys<ObjTyp, "username" | "age1" | "gender"> // K extends keyof T 只会得到包含Keyof T的联合类型。K中多余的类型将会删除

接口构造函数和接口函数类型

// 可以作为类类型
interface ConstructorInter {
  new (...args: any[]): any
}

// 可以作为函数类型
interface FnInter {
  (...args: any[]): any
}

泛型工厂函数

可以通过通用的工厂函数来创建不同的对象,这个装饰器中非常有用。

class Person {
  constructor() {
    console.log("初始化...")
  }
}


function createFactoryConstructor(constructor: ConstructorInter) {
  console.log("前面补充部分")
  new constructor()
  console.log("后面补充部分")
}

createFactoryConstructor(Person)

extends

在ts中extends一般都会用于泛型的约束。配合?:三元运算符一起使用。前者是后者的子类型表示成立。

T extends U ? X : Y

注意,如果需要判断的类型是一个联合类型,那么条件运算符会展开这个联合类型。

(A|B) extends U ? X : Y

// 等同于

(A extends U ? X : Y) |
(B extends U ? X : Y)

但是这只限于在泛型中使用,如果直接传递联合类型他还是会整体比较。 例如下方的Test2

type A<T> = T extends number | string ? T : never
type A2 = A<number | string | boolean> // string | number

type Test2 = string | boolean  extends string ? string : never // never

keyof

keyof接受一个对象(类,接口,类型别名等等) 类型作为参数,返回该对象的所有键名组成的联合类型。

type A = {
  name: string
}

type OptionA = {
  [Prop in keyof Array<number>]?: Array<number>[Prop];
};


type OptionArray = {
  [Prop in keyof Array<number>]?: Array<number>[Prop];
};

// 返回数组属性方法得联合类型
type ArrayItem = keyof Array<number>
// 由于 JavaScript 对象的键名只有三种类型,所以返回string | number | symbol联合类型
type AnyItem = keyof any // string | number | symbol

typescript中的in操作符的特殊用法

在typescript中in操作符,不但可以判断属性是否在该对象中,还可以用来取出(遍历)联合类型的每一个成员类型。例如我们经常使用[K in keyof Inter]来遍历Inter接口中的每个成员。

例如映射某个接口全部成员

interface CommonInter {
  name: string,
  age: number,
  gender: number
}
// 将CommonInter接口成员映射成可选成员
type OptionCommonInter = {
  [Prop in keyof CommonInter]?: CommonInter[Prop];
};

infer

infer表示在extends条件语句中以占位符出现的等到使用时才推断出来的数据类型。它通常跟条件运算符一起使用。

type FnType = (params: Person) => string

// 只要定义了infer变量后面的表达式就可以使用infer变量了。
type ReturnType<T> = T extends (params: any) => infer I ? I : never
type ParamsType<T> = T extends (params: infer I) => any ? I : never

const returnfn: ReturnType<FnType> = "zh" // string
const paramsfn: ParamsType<FnType> = {
  name: "zh"
} // Person

使用infer关键字可以减少泛型变量的定义。infer后面的变量就是一个占位符,不需要提前定义。

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

// 等价于

type Flatten<Type, Item> = Type extends Array<Item> ? Item : Type;

例如vue3中的UnwrapRef类型。

export type UnwrapRef<T> = T extends ShallowRef<infer V>
  ? V
  : T extends Ref<infer V>
  ? UnwrapRefSimple<V>
  : UnwrapRefSimple<T>

配合模板字符串使用。

// infer 占位前面的字符串,过滤掉尾部字符串。我们一定要清楚在ts中值也可以当作类型使用。
type RemoveTail<S extends string, Tail extends string> =
  S extends `${infer P}${Tail}` ? P : S;
  
const remove: RemoveTail<"zhllm", "llm"> = "zh"

typeof

在ts它可以正确的返回变量的类型。

const a1 = {name: "zh"}
const a2 = [1,2,3]
class a3 {
  constructor(private name: string){}
};
function a4(a: string) {}
type A1 = typeof a1 // {name: string}
type A2 = typeof a2 // number[]
type A3 = typeof a3 // class
type A4 = typeof a4 // (a: string) => void

let a33: A3 = a3

增加接口属性

给指定接口类型中添加一个属性,这个在开发中挺常用的,一般都是通过extends自己去重新创建一个接口继承自其他接口类型,比较冗余,所以下面这种通用的别名就比较简单了。

// 接口增加属性
interface CommonInter {
  name: string,
  age: number,
  gender: number
}

type AddCommonInterType<T, K extends string, V> = {
  [Key in keyof T | K]: Key extends keyof T ? T[Key] : V
}

const common: AddCommonInterType<CommonInter, "friends", CommonInter[]> = {
  name: "zh",
  age: 24,
  gender: 1,
  friends: [
    {
      name: "llm",
      age: 23,
      gender: 2,
    }
  ]
}

删除接口中的属性

这个和给指定接口类型中添加一个属性一样的用处,在开发中可以简化我们的类型定义。

// 删除接口中的属性
type RemoveCommonInterType<T, K> = {
  // 使用as在进行一层约束,从而排除K得属性
  [Key in keyof T as Key extends K ? never : Key]: T[Key]
}

type KeyList<T> = T extends any ? T : never

const keys:KeyList<keyof RemoveCommonInterType<CommonInter, "gender">> // name | age

const removeCommon: RemoveCommonInterType<CommonInter, "gender"> = {
  name: "zh",
  age: 1
}

通用对象键类型

  • number 支持number, symbol作为键
  • string 支持string, number, symbol作为键
  • symbol 支持symbol作为键。
type Record<K extends keyof any, T> = {
  // in 表示迭代联合类型
  [P in K]: T
}

type ObjType = Record<string, string>

// string类型作为key,string, number, symbol类型的都可以作为key
const obj: ObjType = {
  1: "zh",
  name: "zh",
  [Symbol()]: "zh"
}

映射类型约束

通过类型断言去对映射的对象类型的key进行约束,从而排除一些内容。 Omit类型就是通过这种方法实现的。

// 从接口或者类中排除指定属性
type MyOmit<T, U extends keyof T> = {
  [P in keyof T as P extends U ? never : P]: T[P]
}

泛型中的this

他就表示当前泛型类型。主要是可以让对象进行链式调用。

interface Inter<T> {
  obj?: this; // Inter<number> this就标识当前泛型
  name: string;
  value: T;
}


const obj: Inter<number> = {
  name: "zh",
  value: 20,
  obj: {
    name: "llm",
    value: 21
  }
}

参考

往期年度总结

往期文章

专栏文章

结语

本篇文章到此就结束了,欢迎在评论区交流。

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏✍️评论,   支持一下博主~

转载自:https://juejin.cn/post/7366457260414713893
评论
请登录