一. infer
infer 的定义:infer 表示在 extends 条件语句中以占位符出现的用来修饰数据类型的关键字,被修饰的数据类型等到使用时才能被推断出来。
infer 占位符式的关键字出现的位置:通常infer出现在以下三个位置上。
- infer 出现在extends 条件语句后的函数类型的参数类型位置上
interface Customer {
custname: string
buymoney: number
}
// 1
type custFuncType = (cust: Customer) => void
type inferType<T> = T extends (x: infer P) => any ? P : T
type inferRes = inferType<custFuncType> // Customer
// 2
type custFuncType = (cust: Customer) => number
type inferType<T> = T extends (x: infer P) => string ? P : T
type inferRes = inferType<custFuncType> // (cust: Customer) => number
- infer 出现在 extends 条件语句后的函数类型的返回值类型上
// 1
interface Customer {
custname: string
buymoney: number
}
type custFuncType = (cust: Customer) => string
type inferType<T> = T extends (x: any) => infer P ? P : T
type inferRes = inferType<custFuncType> // string
// 2
interface Customer {
custname: string
buymoney: number
}
type custFuncType = (cust: Customer) => string
type inferType<T> = T extends (x: string) => infer P ? P : T
type inferRes = inferType<custFuncType> // (cust: Customer) => string
- infer 会出现在类型的泛型具体化类型上。
class Subject {
constructor(public num: number, public project: string) {}
}
let chineseSub = new Subject(100, '语文')
let matchSub = new Subject(100, '数学')
let englishSub = new Subject(100, '英语')
// 此处的Subject作为类型使用
let setSub = new Set<Subject>([chineseSub, matchSub, englishSub])
// 此处的Subject作为变量使用
type ss = typeof setSub
type eleType<T> = T extends Set<infer V> ? V : never
let res: eleType<ss>
export {}
二. 类的类型定义
class Person {}
// 通用
type common<T> = new (...args: any[]) => T
let mm: common<Person> = Person
// 1
let p1: new (name: string, phone: number, notional: string) => Person = Person
// 2
let p2: new (...args: any[]) => any = Person
// 3
let p3: typeof Person = Person
三. 泛型定义类工厂函数
class TestClass {
constructor(public name: string, public age: number) { }
eat() {
console.log(this.name + 'haha');
}
}
type ConstructorType<T> = new (...args: any[]) => T
type ArgumentsType<T extends new (...args: any[]) => any> = T extends new (...args: infer R) => any ? R : never
// args = [name: string, age: number] 元组
function createInstance<T, V extends new (...args: any[]) => any>(Constructor: ConstructorType<T>, ...args: ArgumentsType<V>) {
return new Constructor(...args)
}
let p = createInstance<TestClass, typeof TestClass>(TestClass, 'kk', 18)
p.eat() // kkhaha
四. Ref的infer应用(unref)
function unref<T>(ref: T): T extends Ref<infer V> ? V : T {
return isRef(ref) ? (ref.value as any) : ref
}
// 基本类型
let baseRef = ref(23) // { value: 23 }
// 一个基本类型会转化为Ref<number>
// let baseUnref = unref<Ref<number>>(baseRef)
let baseUnref = unref(baseRef)
// 对象
let objectRef = ref({ name: 'kk' })
let objectUnref = unref(objectRef)
// 当使用对象作为参数时,在使用ref时不会将对象转化为Ref对象,所以直接返回
isRef 类型判断的实现
五. 高阶类型
1. Extract
排除不成立的,寻找重合部分
type Extract<T, U> = T extends U ? T : never
- 父子类
定律一:子类extends 父类=》子类extends 父类返回true=>返回T类型
class People {
public name: string = 'jj'
}
class ChinesePeople extends People {
public addr: string = 'yy'
}
type Extract<T, U> = T extends U ? T : never
type extractType = Extract<ChinesePeople, People> // ChinesePeople
定律二:父类 extends 子类=>父类 extends 子类返回false 因为父类继承子类本身不成立
但如果希望人为制造一个true 获取到People
那只有子类实例属性或实例方法个数必须和父类一样多
class People {
public name: string = 'jj'
}
class ChinesePeople extends People {
public addr: string = 'yy'
}
type Extract<T, U> = T extends U ? T : never
type extractType = Extract<People, ChinesePeople> // never
如果两个类都为可选属性,那么如果父类extends
子类也成立:
class People {
public name?: string = 'jj'
}
class ChinesePeople extends People {
private age?: number
}
type Extract<T, U> = T extends U ? T : never
type extractType = Extract<People, ChinesePeople> // People
或者属性一致
class People {
public name: string = 'jj'
}
class ChinesePeople extends People {
public name: string = 'jj'
// private age?: number
// public addr: string = 'yy'
}
type Extract<T, U> = T extends U ? T : never
type extractType = Extract<People, ChinesePeople> // People
- 联合类型
// 取合集
type unionExtractType = Extract<string, string | number> // string
type unionExtractType1 = Extract<string | number, string | number | boolean> // string | number
另一种情况
type unionExtractType2 = Extract<string | number, string> // string
两个合集为或时,会挨个将左侧与右侧进行判断,例如:
string extends string ? // string
number extends string ? // never
所以返回 string
加入将Extract
更改一下
// 将never更改为boolean
type Extract<T, U> = T extends U ? T : boolean
// string | number | boolean
type unionExtractType3 = Extract2<string | number | symbol, string | number>
断言番外
function testFunc(one: string | number, two: string) {
one as string
one as number
one as any
// 单类型可以断言为联合类型
two as string | number | boolean
}
- 函数类型
在返回值相同时,参数有包含关系,第一个参数相同,参数少 extends 参数多成立
type func1 = (name: string, age: number) => string
type func2 = (name: string) => string
// (name: string) => string
type unionFunc = Extract<func2, func1>
Extract应用场景
泛型函数重载:(比较繁琐)
function cross<T extends object, U extends object>(obj1: T, obj2: U): T & U
function cross<T extends object, U extends object, V extends object>(obj1: T, obj2: U, obj3: V): T & U & V
function cross<T extends object, U extends object, V extends object>(obj1: T, obj2: U, obj3?: V) {}
使用Extract
优化:
type Extract<T, U> = T extends U ? T : never
function cross<T, U>(obj1: Extract<T, object>, obj2: Extract<U, object>): T & U
function cross<T, U, V>(obj1: Extract<T, object>, obj2: Extract<U, object>, obj3: Extract<V, object>): T & U & V
function cross<T, U, V>(obj1: Extract<T, object>, obj2: Extract<U, object>, obj3?: Extract<V, object>) {}
深层优化:
type Extract<T, U> = T extends U ? T : never
type CyosTyp<T> = Extract<T, object>
function cross<T, U>(obj1: CyosTyp<T>, obj2: CyosTyp<U>): T & U
function cross<T, U, V>(obj1: CyosTyp<T>, obj2: CyosTyp<U>, obj3: CyosTyp<V>): T & U & V
function cross<T, U, V>(obj1: CyosTyp<T>, obj2: CyosTyp<U>, obj3?: CyosTyp<V>) {}
2. Exclude
与Extract
相反,排除成立的,寻找差异部分
使用Exclude
获取指定属性
interface People {
name: string
age: number
addr: string
male: boolean
}
// 使用keyof获取所有key
type exc1 = Exclude<keyof People, 'name'> // age | addr | male
获取Woker
接口中存在,而Student
接口不存在的属性
interface Student {
name: string
age: number
addr: string
male: boolean
}
interface Worker {
name: string
age: number
addr: string
salary: boolean
}
type exc1 = Exclude<keyof Student, keyof Worker> // 'salary'
3. Record
理解keyof
type dataType = {
name: string,
age: number
}
// 返回key
type oneType<T, U> = T extends keyof U ? T : never
type test = oneType<'name', dataType> // name
// 返回类型
type twoType<T, U> = T extends keyof U ? U[T] : never
type test = twoType<'name', dataType> // string
keyof any
type threeType<T> = T extends keyof any ? T : never
type anyType = keyof any // number | string | symbol
type Customer = {
name: string,
age: number
}
type oneRes = threeType<Customer> // never
type oneRes = threeType<number> // number
type oneRes = threeType<3> // 3 3 extends number true
let str = 'haha'
type oneRes = threeType<typeof str> // string
type oneRes = threeType<'xixi'> // xixi xixi extends number true
let sym = Symbol('ui')
type oneRes2 = threeType<typeof sym> // symbol
Record
type Record<K extends keyof any, T> = {
[P in K] : T
}
// in 代表遍历K的联合类型
约束对象的结构
// 约束key只能为number / string
type test2 = Record<string | number, Student>
如果传入第一个参数为string
,那么代表形成一个key
为字符串的索引类型
interface Student {
name: string
age: number
addr: string
male: boolean
}
type test1 = Record<string, Student>
如果传入第一个参数为number,那么代表形成一个key
为数值的索引类型
interface Student {
name: string
age: number
addr: string
male: boolean
}
type test1 = Record<number, Student>
索引中字符串和数字类型可以进行转换:
[x: string]
可以是字符串,数字,Symbol类型
// 1
type TTT = {
[x: string]: string
}
// 也可以是Symbol
const sym = Symbol('op')
let testObj: TTT = {
3: 'hha',
as: '123',
'rr': '900',
sym: '123'
}
// 2
type TTT = {
[x: number]: string
}
let testObj: TTT = {
3: 'hha',
'1': '123'
}
变种
type Record<K extends keyof any, T> = {
[P in K] : T
}
type Record<T> = {
[P in keyof any]: T
}
因为索字符串引可以替代数字和Symbol类型,所以P会被解析为字符串索引:
可以用来约束数组,因为数组是以数字为索引的对象结构。
let arr: test1 = [{
name: 'kk',
age: 18,
addr: 'ee',
male: true
},
{
name: 'kk',
age: 13,
addr: 'qe',
male: true
}]
数组扁平化
目标:
[{}, {}]
{
1: {},
2: {}
}
- 实现方式一
const goodSymId = Symbol('goodid')
interface dataType {
name: string,
price: number,
[goodSymId]: number
}
let goodList: dataType[] = [
{
name: '苹果',
price: 3,
[goodSymId]: 101
},
{
name: '橘子',
price: 6,
[goodSymId]: 102
},
{
name: '橙子',
price: 9,
[goodSymId]: 103
}
]
type resGoodsType = Record<number, dataType>
let res: resGoodsType = {}
goodList.forEach(goods => {
res[goods[goodSymId]] = goods
})
{
"101": {
"name": "苹果",
"price": 3,
[Symbol(goodid)]: 101
},
"102": {
"name": "橘子",
"price": 6,
[Symbol(goodid)]: 102
},
"103": {
"name": "橙子",
"price": 9,
[Symbol(goodid)]: 103
}
}
Record和object区别
区别1: Record 获取到是索引参数类型,所以可以赋初值为{} 而object也可以,但是再次赋值会出现错误。
区别2:Record是泛型,获取值可以有自动提示功能,而object无法实现自动提示。
Map与Record区别
interface dataType {
name: string,
price: number,
[goodSymId]: number
}
let data = new Map<string, dataType>()
data.set('key', {
name: '苹果',
price: 3,
[goodSymId]: 101
})
4. Pick
仅保留部分属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
interface Book {
book_name: string,
book_price: number,
book_publish: string
}
type PickType = Pick<Book, 'book_name' | 'book_price'>
// {
// book_name: string,
// book_price: number,
//}
5. Omit
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
interface Todo {
title: string
completed: boolean
description: string
}
type test = Omit<Todo, 'description'>
// 拆解
type ExcludeType = Exclude<keyof Todo, 'description'> // 'title' | 'completed'
type PickType = Pick<Todo, 'title' | 'completed'>
// {
// title: string
// completed: boolean
// }
模拟Promis定义ts类型
type ResolveType = (resolve_success: any) => any
type RejectType = (reject_fail: any) => any
class MyPromise {
public resolveFunc!: ResolveType
public rejectFunc!: RejectType
constructor(promiseParams: (resolve: ResolveType, reject: RejectType) => any) {
this.resolveFunc = (resolve_success: any): any => {
console.log('success');
}
this.rejectFunc = (reject_fail: any): any => {
console.log('fail');
}
promiseParams(this.resolveFunc, this.rejectFunc)
}
}
new MyPromise(function (resolve: ResolveType, reject: RejectType) {
resolve('success')
})
函数解构
type CustObjType = { custname: string, degree: number }
type funcType = (one: CustObjType, two: string) => void
function func(fn: funcType) {
fn({ custname: 'kk', degree: 18 }, 'good')
}
// 解构使用CustObjType进行限制
func(function({ degree }: CustObjType, two: string) {
console.log(degree);
})
vuex准备:实现action类型
不使用new来实例化类
class Store<S> {
public state!: S
commit!: CommitType
}
// let store: Store2<string> = {
let { state, commit }: Store2<string> = {
state: 'gg',
commit: (type: string, payload: any) => {}
}
简易actions
type CommitType = (type: string, payload?: any) => void
type ActionsHandler<S, R> = (store: Store<S>, payload?: any) => any
type ActionsTrss<S, R> = {
[Key: string]: ActionsHandler<S, R>
}
class Store<S> {
public state!: S
commit!: CommitType
constructor(storeOptions: StoreOptions<S>) { }
}
interface StoreOptions<T> {
state: T,
actions: ActionsTrss<T, T>
}
function createStore<S>(storeOptions: StoreOptions<S>) {
return new Store<S>(storeOptions)
}
let storeOptions: StoreOptions<string> = {
state: 'kk',
actions: {
add({ commit }, payload) {
commit('food', '宫傲鸡丁')
},
del({ commit, state }) {
}
}
}
原文链接:https://juejin.cn/post/7348709938023645224 作者:小黄瓜没有刺