看懂ts类型体操之前,你需要先知道这些
最近也是在开发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