Skip to content

TS 泛型编程

类型的参数化

ts
// 1. 理解形参和实例参数化,单数参数的类型是固定的
function foo(name: string, age: number) {}

foo('XQ', 19)
foo('XB', 20)

// 2. 定义一个函数: 将传入的函数内容返回
function bar<Type>(arg: Type) {
	return arg
}

// 2.1 完整写法
const res1 = bar<string>('abc')
const res2 = bar<number>(123)

// 2.2 省略写法
const res3 = bar('aaa')
const res4 = bar(123)

export {}

泛型练习

ts
// 元组: useState 函数
function useState<Type>(initialState: Type): [Type, (newState: Type) => void] {
	let state = initialState
	function setState(newState: Type) {
		state = newState
	}
	return [state, setState]
}

// 初始化count
const [count, setCount] = useState<number>(100)

传入多个类型:

ts
function foo<Type, Element>(arg1: Type, arg2: Element) {}

const res = foo(10, 20)

平时在开发中我们可能会看到一些常用的名称:

  • T:Type 的缩写,类型
  • K、V:key 和 value 的缩写,键值对
  • E:Element 的缩写,元素
  • O:Object 的缩写,对象

泛型接口

ts
interface ISj<Type> {
	name: Type
	age: number
}

const sj: ISj<string> = {
	name: 'sj',
	age: 19,
}

const sj2: ISj<number> = {
	name: 99,
	age: 19,
}

export {}

泛型类

ts
class Point<Type = number> {
	x: Type
	y: Type
	constructor(x: Type, y: Type) {
		this.x = x
		this.y = y
	}
}

const p1 = new Point(10, 10)
const p2 = new Point<string>('123', '321')
console.log(p1, p2) // Point { x: 10, y: 10 } Point { x: '123', y: '321' }

泛型约束

ts
interface ILength {
	length: number
}

function getLength(arg: ILength) {
	return arg.length
}

const length1 = getLength('aaa')
const length2 = getLength(['aaa', 'bbb', 'ccc'])

// 获取传入的内容,这个内容必须有length属性
// Type 相当于一个变量,用于记录本次调用的类型,所以真个函数执行周期中,一直保留着参数的类型
function getInfo<Type extends ILength>(args: Type): Type {
	return args
}
const info1 = getInfo('aaa')
const info2 = getInfo(['aaa', 'bbb', 'ccc'])
// const info3 = getInfo(11111) // 报错
console.log(info1, info2) // aaa [ 'aaa', 'bbb', 'ccc' ]

泛型参数使用约束

ts
// keyof 拿到所以得 key
function getObjectProperty<O, K extends keyof O>(obj: O, key: K) {
	return obj[key]
}

const info = {
	name: 'sj',
	age: 19,
}

const names = getObjectProperty(info, 'name')
console.log(names) // sj

映射类型

基本使用:

ts
type MapPerson<Type> = {
	[Property in keyof Type]: Type[Property]
}

interface IPerson {
	name: string
	age: number
}

type NewPerson = MapPerson<IPerson>

export {}

映射类型修饰符:

  • 一个是 readonly,用于设置属性只读;
  • 一个是 ? ,用于设置属性可选;
ts
type MapIPerson<Type> = {
	readonly [Property in keyof Type]-?: Type[Property]
}

interface IPerson {
	name?: string
	age?: number
}

type NewPerson = MapIPerson<IPerson>
/* 
type NewPerson = {
	readonly name: string
	readonly age: number
} 
*/

const p: NewPerson = {
	name: 'sj',
	age: 19,
}

console.log(p) // { name: 'sj', age: 19 }
// p.name = 'XQ' // 报错

类型体操

类型编程的类型系统,这种类型编程系统为 TypeScript 增加了很大的灵活度,同时也增加了它的难度:

  • 如果你不仅仅在开发业务的时候为自己的 JavaScript 代码增加上类型约束,那么基本不需要太多的类型编程能力;
  • 但是如果你在开发一些框架、库,或者通用性的工具,为了考虑各种适配的情况,就需要使用类型编程;

很多开发者为了进一步增强自己的 TypeScript 编程能力,还会专门去做一些类型体操的题目:

内置工具

条件类型

很多时候,日常开发中我们需要基于输入的值来决定输出的值,同样我们也需要基于输入的值的类型来决定输出的值的类型

SomeType extends OtherType ? TrueType : FalseType;

ts
type IDType = number | string

// 判断 number 是否是 extendsIDType
type ResType = boolean extends IDType ? true : false

// 案例: 函数的重载
function sum<T extends number | string>(
	num1: T,
	num2: T
): T extends number ? number : string

function sum(num1: any, num2: any) {
	return num1 + num2
}
const res = sum(123, 321)
const res2 = sum('123', '321')
console.log(res, res2) // 444 '123321'

在条件类型中推断

ts
type calcFnType = (num1: number, num2: number) => number

function foo() {
	return 'abc'
}

// 获取一个函数的返回值类型: 内置工具 ReturnType
type CalcReturnType = ReturnType<calcFnType> // number
type FooReturnType = ReturnType<typeof foo> // string

分发条件类型

ts
type toArray<T> = T extends any ? T[] : never

type NumArray = toArray<number>
// number[] | string[]
type ArrayAndString = toArray<number | string>

Partial

用于构造一个 Type 下面的所有属性都设置为可选的类型

ts
interface ISj {
	name: string
	age: number
	like?: string[]
}

// 转换为可选的
type ISjOptional = Partial<ISj>

// 转换为可选的
type ISjOptional = Partial<ISj>

// 类型体操
type SJPartal<T> = {
	[P in keyof T]?: T[P]
}

Required

用于构造一个 Type 下面的所有属性全都设置为必填的类型,这个工具类型跟 Partial 相反。

ts
interface ISj {
	name: string
	age: number
	like?: string[]
}

// 转换为必选的
type ISjOptional = Required<ISj>

// 类型体操
type SJPartal<T> = {
	[P in keyof T]-?: T[P]
}

Readonly

用于构造一个 Type 下面的所有属性全都设置为只读的类型,意味着这个类型的所有的属性全都不可以重新赋值

ts
interface ISj {
	name: string
	age: number
	like?: string[]
}

// 转换为只读的
type ISjOptional = Readonly<ISj>

// 类型体操
type SJPartal<T> = {
	readonly [P in keyof T]: T[P]
}

Record

用于构造一个对象类型,它所有的 key(键)都是 Keys 类型,它所有的 value(值)都是 Type 类型。

ts
interface IXB {
	name: string
	age: number
	like?: string[]
}

type t1 = '大理' | '昆明' | '曲靖'
type IXBs = Record<t1, IXB>
/* 
type XBs = {
    大理: IXB;
    昆明: IXB;
    曲靖: IXB;
} */
const iXBs: IXBs = {
	大理: {
		name: 'xxx',
		age: 19,
	},
	昆明: {
		name: 'yyy',
		age: 20,
	},
	曲靖: {
		name: 'zzz',
		age: 30,
	},
}
console.log(iXBs)
/* {
  '大理': { name: 'xxx', age: 19 },
  '昆明': { name: 'yyy', age: 20 },
  '曲靖': { name: 'zzz', age: 30 }
} */

Pick

用于构造一个类型,它是从 Type 类型里面挑了一些属性 Keys

ts
interface IXQ {
	name: string
	age: number
	like?: string[]
}

type IXQs = Pick<IXQ, 'name' | 'age'>
/* type IXQs = {
    name: string;
    age: number;
} */

// 类型体操
type IXQPick<T, K extends keyof T> = {
	[P in K]: T[P]
}

Omit

用于构造一个类型,它是从 Type 类型里面过滤了一些属性 Keys

ts
interface IXQ {
	name: string
	age: number
	like?: string[]
}

type IXQs = Omit<IXQ, 'name' | 'age'>
/* 
type IXQs = {
    like?: string[] | undefined;
}*/

// 类型体操
type IOmit<T, K extends keyof T> = {
	[P in keyof T as P extends K ? never : P]: T[P]
}

Exclude

用于构造一个类型,它是从 UnionType 联合类型里面排除了所有可以赋给 ExcludedMembers 的类型。

ts
type like = '抽烟' | '喝酒' | '打麻将'

type IXQs = Exclude<like, '喝酒'>
// type IXQs = "抽烟" | "打麻将"

// 类型体操
type IExclude<T, E> = T extends E ? never : T

Extract

用于构造一个类型,它是从 Type 类型里面提取了所有可以赋给 Union 的类型。

ts
type like = '抽烟' | '喝酒' | '打麻将'

type IXQs = Extract<like, '喝酒'>
// type IXQs = "喝酒"

// 类型体操
type IExclude<T, E> = T extends E ? T : never

NonNullable

用于构造一个类型,这个类型从 Type 中排除了所有的 null、undefined 的类型

ts
type like = '抽烟' | '喝酒' | '打麻将' | null | undefined

type IXQs = NonNullable<like>
// type IXQs = "抽烟" | "喝酒" | "打麻将"

// 类型体操
type INonNullable<T> = T extends null | undefined ? never : T

ReturnType

用于构造一个含有 Type 函数的返回值的类型

ts
type like = (num1: number, num2: number) => number

type IXQs = ReturnType<like>
// type IXQs = number

InstanceType

用于构造一个由所有 Type 的构造函数的实例类型组成的类型。

ts
class Person {}

const p1: Person = new Person()

// typeof Person: 构造函数具体类型
// InstanceType: 构造函数实例对象类型
type SJPerson = InstanceType<typeof Person>

// 案例
function factory<T extends new (...args: any[]) => any>(
	ctor: T
): InstanceType<T> {
	return new ctor()
}
const p3 = factory(Person)