Typescript泛型专题

538 阅读4分钟

常见泛型变量代表

  • T(Type):在定义泛型时通常用作第一个类型变量名称,但实际上 T 可以用任何有效名称代替
  • K(Key):表示对象中的键类型;
  • V(Value):表示对象中的值类型;
  • E(Element):表示元素类型。

<>中的类型是传递类型,比如

写法举例:

  1. 泛型接口
interface Teacher<T> {
  (arg: T): T;  //ok
  [key: T]: T;  //error
}

可见泛型不能引用在「索引签名」

  1. 泛型函数
function introduce<T,U>(name:T,age:U): T {
	return name
}
console.log(introduce<string, number>("hannie",18));

注:返回值类型和参数类型必须在传递类型中找到,此处返回值类型是T,返回值类型是T和U,传递类型是<T,U>

  1. 泛型类
class Teacher<T> {
  age: T;
  add: (x: T, y: T) => T;
}

let teacher = new Teacher<number>();
teacher.age = 0;
teacher.add = function (x, y) {
  return x + y;
};

下面细讲泛型工具类型

typeof:推算变量类型

  • 推算字符串遍历类型
let str1 = 'fooooo'
type Type1 = typeof str1 // type string
  • 推算接口类型
interface Person {
  name: string;
  age: number;
}

const sem: Person = { name: 'semlinker', age: 30 };
type Sem= typeof sem; // -> Person

function toArray(x: number): Array<number> {
  return [x];
}

type Func = typeof toArray; // -> (x: number) => number[]

keyof:获取对象的 key 值集合

  • interface对象key
interface Person {
    name: string;
    age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join" 
type K3 = keyof { [x: string]: Person };  // string | number  
  • type对象key
type Teacher = {
  id: string;
  name: string;
};

type TeacherKeys = keyof Teacher; //"id" | "name"

let a: TeacherKeys =  'id' || 'name'  // ok
let a: TeacherKeys =  'other'  //error

这样就轻松获取到了对象 key 值的联合类型:"foo" | "bar"。

它也可以用在遍历中:

type Obj = {
  foo: number;
  bar: string;
}

type Copy = {
  [K in keyof Obj]: Obj[K]
}

Copy 得到和 Obj 一模一样的类型

extends:三目运算和继承

  1. 在 interface 中表示类型扩展
// 表示类型扩展
interface A {
  a: string
}

interface B extends A { // { a: string, b: string }
  b: string
}
  1. 在条件类型语句中表示布尔运算
 type User<T> =  'T extends'
 type u = User<boolean>  //<>中任何类型都行
 let a: u = 'T extends'  //User一样的值
type User<T> = T extends string ? string : never
type A = User<number> // never
type B = User<string> // string
type C = User<'hhy'> // string
let b:B = '213'
  
type User<T> = T extends string ? 'string' : never
type A = User<number> // never
type B = User<string> // string
type C = User<'hhy'> // string
let b:B = 'string' //不能是其他
  1. 在泛型中起到限制的作用
type Foo<T extends object> = T
type A = Foo<number> // 类型“number”不满足约束“object”。
type B = Foo<string> // 类型“string”不满足约束“object”。
type C = Foo<{}> // OK
  1. 在 class 中表示继承
class A {}
class B extends A {}

注:使 A extends B 在布尔运算或泛型限制中成立的条件是 A 是 B 的子集,也就是 A 需要比 B 更具体,至少是跟 B 一样。

type K = '1' extends '1' | '2' ? 'true' : 'false' // "true"
type L = '1' | '2' extends '1' ? 'true' : 'false' // "false"

type M = { a: 1 } extends { a: 1, b: 1 } ? 'true' : 'false' // "false"
type N = { a: 1, b: 1 } extends { a: 1 } ? 'true' : 'false' // "true"

infer:声明一个类型变量,也可以理解为在条件类型中给了它一个占位符,动态推导泛型

什么场景下还需要动态推导?通常是需要通过传入的泛型参数去获取新的类型,这和直接定义一个新的泛型参数不一样。

注:infer 一定是出现在条件类型中的。

type Get<T> = T extends infer R ? R: never

infer R 的位置代表了一个未知的类型,可以理解为在条件类型中给了它一个占位符,然后就可以在后面的三元运算符中使用它。

type T = Get<number>

// 经过计算
type Get<number> = number extends infer number ? number: never
// 得到
> number

它的使用非常灵活,它也可以出现在泛型位置

type Unpack<T> = T extends Array<infer R> ? R : T
type NumArr = Array<number>
type U = Unpack<NumArr>

// 经过计算
type Unpack<Array<number>> = Array<number> extends Array<infer R> ? R : T

// 得到
> number

in:在类型中表示类型映射,和索引签名的写法有些相似

遍历枚举类型

type Keys = "a" | "b" | "c"
  
type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }  

type Props = {
  [key in 'count' | 'id']: number
}
const props1: Props = { // OK
  count: 1,
  id: 1
}

const props2: Props = {
  count: '1', // ERROR
  id: 1
}

ReturnType:获取方法的返回值类型

type A = (a: number) => string
type B = ReturnType<A> // string

Parameters:获取方法的参数类型

type EventListenerParamsType = Parameters<typeof window.addEventListener>;
// [type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined]

Exclude:计算在 T 中而不在 U 中的类型

type A = number | string
type B = string
type C = Exclude<A, B> // number

Extract:计算 T 中可以赋值给 U 的类型

type A = number | string
type B = string
type C = Extract<A, B> // string

NonNullable:从类型中排除 null 和 undefined

type A = {
  a?: number | null
}
type B = NonNullable(A['a']) // number

Record:定义键类型为 Keys、值类型为 Values 的对象类型

enum ErrorCodes {
  MethodError = 405,
  ServerError = 500,
}

const ErrorMessageMap: Record<ErrorCodes, string> = {
  [ErrorCodes.MethodError]: '方法错误',
  [ErrorCodes.ServerError]: '服务器错误'
}

Partial: 将类型定义的属性变成可选

type Teacher = {
  id?: string,
  gender: 'male' | 'female'
}

type PartialTeacher =  Partial<Teacher>  // { id?: string, gender?: 'male' | 'female'}

function createTeacher (teacher: PartialTeacher = { gender: 'male' }) {}

Required:将对象类型的属性都变成必须,与Partial 的作用相反

type Teacher = {
  id?: string,
  gender: 'male' | 'female'
}

type RequiredTeacher = Required<Teacher> 

function showInfo (user: RequiredTeacher) {
  console.log(user.id) // 1
  console.log(user.gender) //male
}
showInfo({id:'1',gender:'male'})

Readonly:将对象类型的属性都变成只读

type Teacher = {
  id?: string,
  gender: 'male' | 'female'
}

type ReadonlyTeacher = Readonly<Teacher> 

const user: ReadonlyTeacher = {
  id: '1',
  gender: 'male'
}

user.gender = 'femail' // error ,因为它是只读属性。

Pick:挑选类型中的部分属性

type Person = {
  age: number
  name: string
  city: string
  address: string
  province: string
}

type partInfo = Pick<Person, 'city' | 'address'> 

const region: partInfo = {
  city: 'beijingg',
  address: 'chaoyang'
}

Omit :结合了 Pick 和 Exclude,将忽略对象类型中的部分 keys

type Person = {
  age: number
  name: string
  city: string
  address: string
  province: string
}

type partInfo = Omit<Person, 'city' | 'address'> 

const region: partInfo = {
  age: 18,
  name: 'hannie',
  province: 'beijing'
}  //除了Omit中的,必须写全其他,不然会报错