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)