TS操作符,学好TS的第一步
序言
最近开始学习TS,发现了很多困难,过了这道坎以后,发现TS的操作符就像拼音是汉字的基础一样,学好TS操作符,是学好TS的重中之重,我们今天就来详细的了解一下TS的操作符。
typeof
在 TypeScript中,typeof
操作符可以用来获取一个变量的声明,或是对象的类型。我们看两个小例子
//例子1
interface People {
name: string;
age: number;
}
const variableDai: People = { name: 'coolFish', age: 24 };
type formDai= typeof variableDai; // -> People
//例子2
function toArray(x: number): Array<number> {
return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]
小结:要注意的是,例子1中我们定义了一个常量 variableDai 然后获取他的类型,赋值给了类型 formDai。
Keyof
keyof
操作符可以用来一个对象中的所有 key 值, 返回的是这些key值得联合类型。
interface Person {
name: string;
age: number;
}
type allKey = keyof Person; // "name" | "age"
当然我们可以利用这个特性来做一些特殊的操作,例如遍历一个空数组,返回一个数组所拥有的所有属性的联合类型
interface Person {
name: string;
age: number;
}
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
type K3 = keyof { [x: string]: Person }; // string | number
因为这里的键是索引类型,所以是 string | number
in
in
用来遍历枚举类型
type Keys = "a" | "b" | "c"
type Obj = {
[p in Keys]: any
}
//{ a: any, b: any, c: any }
infer
在条件类型语句中,可以用infer 声明一个类型变量,并且对它进行使用。
//返回数组的第一项
type Head<T extends Array<any>> = T extends [head : infer H, ...rest : any[]] ? H : never;
// 测试用例
type H0 = Head<[]> // never
type H1 = Head<[1]> // 1
type H2 = Head<[3, 2]> // 3
上例的原理是,我们用infer定义一个类型变量为第一项,然后类型收窄,就获得了第一项。
extends
当我们定义的泛型不想过于灵活,或者想继承某些类的时候,我们可以通过 extends 关键字添加泛型约束
interface mustLength {
length: number;
}
function mustTakeLength<T extends mustLength>(arg: T): T {
console.log(arg.length);
return arg;
}
mustTakeLength(3)// Error, number doesn't have a .length property
loggingIdentity({length: 10, value: 3});//10
我们定义了一个 mustLength 的接口,然后 入参受该接口约束,必须有一个 length 属性。否则就会报错
我们再看一个例子,来理解一下extends
type User = {
id: number;
kind: string;
};
function makeCustomer<T extends User>(u: T): T {
// Error(TS 编译器版本:v4.4.2)
// Type '{ id: number; kind: string; }' is not assignable to type 'T'.
// '{ id: number; kind: string; }' is assignable to the constraint of type 'T',
// but 'T' could be instantiated with a different subtype of constraint 'User'.
return {
id: u.id,
kind: 'customer'
}
}
上面的代码为什么会报错?因为 T 受 User 约束,必须拥有 User中的类型,但不局限只拥有,也能拥有更多的类型,返回只返回了两种,如果有更多类型,就会出问题,所以报错,解决方案如下
type User = {
id: number;
kind: string;
};
// T assignable User
function makeCustomer<T extends User>(u: T): T {
return {
...u,
id: u.id,
kind: 'customer'
}
}
Partial
Partial<T>
的作用就是将某个类型里的属性全部变成可选,该方法是联合了 keyof 和 in实现的。
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface User {
name:string,
age:number,
department:string
}
type optional = Partial<User>
/**type optional = {
name?: string | undefined;
age?: number | undefined;
department?: string | undefined;
}**/
Required
Required<T>
的作用就是将某个类型中的属性全部变为必选,具体实现和Partial类似
type Required<T> = {
[P in keyof T]-?: T[P];
};
与 Partial 不同的就是,Partial 是加上 ? 而这里是移除 ?。
Readonly
Readonly<T>
的作用是将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Record
Record<K extends keyof any,T>
的作用是将 K 中所有的属性的值转换为 T 类型
type Record<K extends keyof any, T> = {
[P in K]: T;
};
小结:将K 中有类型的属性进行遍历,再将每个 Key 赋值 T类型 实例
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
name:string,
age:number,
}
type IPets = Record<petsGroup, IPetInfo>;
const animalsInfo:IPets = {
dog:{
name:'dogName',
age:2
},
cat:{
name:'catName',
age:3
},
fish:{
name:'fishName',
age:5
}
}
Pick
Pick<T,K extends keyof T>
的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
T 是多类型,K 是T的部分类型,然后遍历K拿到键,再去T里拿到相应的值。
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false
};
Exclude
Exclude<T,U>
的作用是将某个类型中属于另一个的类型移除掉
type Exclude<T, U> = T extends U ? never : T;
如果 T 能赋值给 U 类型的话,那么就会返回 never 类型,否则返回 T 类型。最终实现的效果就是将 T 中某些属于 U 的类型移除掉。这里主要是利用了 never 的特性,never 是最底层类型,联合任意类型等于那个类型,已达到移除的效果。
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
Omit
Omit<T, K extends keyof any>
的作用是使用 T 类型中除了 K 类型的所有属性
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Omit<Todo, "description">;
const todo: TodoPreview = {
title: "Clean room",
completed: false
};
我们要实现上面的结果,可以利用 Pick 和 Exclude ,Pick可以挑选出我们想要的,Exclude可以排除掉我们不想要的,所以我们只需要先排除掉 description,再从 Todo 里面 挑选剩余的,就能完成,如下是实现的代码。
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
转载自:https://juejin.cn/post/7010338170592051207