TypeScript 工具类型
1. typeof
1.1 判断类型
typeof
关键字可以用于判断变量的类型,如:"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
。
类型保护是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内。
typeof
关键字就是其中一种类型保护
function test(val: string | number) {
// val.toUpperCase(); // Error 类型“number”上不存在属性“toUpperCase”
if (typeof val === 'string') {
console.log(val.toUpperCase());
}
if (typeof val === 'number') {
console.log(val.toFixed(1));
}
}
1.2 获取变量声明或对象的类型
interface Person {
name: string;
age: number;
}
const sem = { name: 'semlinker', age: 33 };
type Sem = typeof sem; // 等同于 Person接口定义的类型
function toArray(x: number): Array<number> {
return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]
2. keyof
该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
interface Person {
name: string;
age: number;
}
type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"...等数组方法
const bar: K2 = 'concat';
// const bar1: K2 = 'add'; // Error 类型"add"不是数组的方法名
type K3 = keyof { [x: string]: Person }; // string | number
在 TypeScript 中支持两种索引签名,数字索引和字符串索引:
interface StringArray {
// 字符串索引 -> keyof StringArray => string | number
[index: string]: string;
}
type T1 = keyof StringArray; // type T1 = string | number
interface StringArray1 {
// 数字索引 -> keyof StringArray1 => number
[index: number]: string;
}
type T2 = keyof StringArray1; // type T2 = number
3. in
3.1 判断属性是否在对象里
类型保护是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内。
in
关键字就是其中一种类型保护。
interface Admin {
name: string;
privileges: string[];
}
interface Employee {
name: string;
startDate: Date;
}
type UnknownEmployee = Employee | Admin;
function printEmployeeInformation(emp: UnknownEmployee) {
console.log('Name: ' + emp.name);
// console.log(emp.privileges); // Error 类型“Employee”上不存在属性“privileges”
// 同理
// console.log(emp.startDate); // Error类型“Admin”上不存在属性“startDate”
if ('privileges' in emp) {
console.log('Privileges: ' + emp.privileges);
}
if ('startDate' in emp) {
console.log('Start Date: ' + emp.startDate);
}
}
3.2 遍历枚举类型
type Keys = 'a' | 'b' | 'c';
type Obj = {
[p in Keys]: any;
}; // -> { a: any, b: any, c: any }
type Student = {
name: string;
age: number;
school: string;
};
type Obj1 = {
[p in keyof Student]: string;
}; // { name: string; age: string; school: string; }
enum StyleStatus {
primary = 'primary',
success = 'success',
error = 'error',
warn = 'warn'
}
type Obj2 = {
[p in keyof typeof StyleStatus]: string;
}; // { readonly primary: string; readonly success: string; readonly error: string; readonly warn: string;}
4. extends
extends
不仅可以用于继承操作,还可以检验是否拥有其属性。
示例:
我们知道字符串
和数组
拥有length
属性,但number
没有这个属性。
const calcArrayDemo = <T>(data: T): number => {
return data.length; // Error 类型“T”上不存在属性“length”。
};
我们可以进行约束,只有拥有length
属性的才能作为该函数参数:
const calcArray = <T extends { length: number }>(data: T): number => {
return data.length;
};
calcArray([1, 2]);
calcArray(2); // Error
calcArray('nihao');
calcArray({ a: '你', length: 8 });
5. infer
在条件类型语句中,可以用 infer
声明一个类型变量并且对它进行使用。
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
function testReturnType(number: number) {
return number;
}
let a: ReturnType<typeof testReturnType> = '2'; // Error 不能将类型“string”分配给类型“number”。
let b: ReturnType<typeof testReturnType> = 2;
let c: ReturnType<() => string> = '2';
以上就是ReturnType
工具类型的实现,infer R
就是声明一个变量来承载传入函数签名的返回值类型。
其他示例:
type Info<T> = T extends { a: infer U; b: infer U } ? U : never;
type Props = Info<{ a: string; b: number }>; // Props类: string | number
type Props1 = Info<number>; // Props1类型: never
type Props2 = Info<{ a: boolean }>; // Props2类型: never
type Person = {
name: string;
age: number;
};
type Tools = {
function: string;
composition: string;
};
type Props3 = Info<{ a: Person; b: Tools; c: number }>; // Props3类型: Person | Tools
6.Partial
Partial<T>
的作用就是将某个类型里的属性全部变为可选项 ?
。
Partial的实现:
type Partial<T> = { [P in keyof T]?: T[P] | undefined; }
示例:
interface Person {
name: string;
age: number;
address: string;
}
type demo = Partial<Person>;
// type demo = { name?: string | undefined; age?: number | undefined; address?: string | undefined; }
7.Required
Required<T>
作用:将所有属性变为必选的,与 Partial
相反
interface Person {
name: string;
age?: number;
address?: string;
}
type demo = Required<Person>;
// type demo = { name: string; age: number; address: string; }
8. Readonly
Readonly<T>
作用:将所有属性都加上 readonly 修饰符来实现。也就是说无法修改
interface Person {
name: string;
age: number;
address: string;
}
type demo = Readonly<Person>;
// 类型: { readonly name: string;readonly age: number;readonly address: string; }
const stu: demo = {
name: '小明',
age: 13,
address: '住址'
};
stu.address = '街道'; // 无法为“address”赋值,因为它是只读属性。
9. Record
Record
的实现:
type Record<K extends string | number | symbol, T> = { [P in K]: T; }
作用:将 K
中所有的属性的值转化为 T
类型。
示例:
interface Item {
part: string;
count: number;
}
type demo = Record<string, Item>;
const test: demo = {
itemA: {
part: 'A',
count: 1
},
itemB: {
part: 'B',
count: 2
},
itemC: {
part: 'C',
// name: 'C模块', // Error: “name”不在类型“Item”中
count: 3
}
};
// 复杂用法
type lan = 'JS' | 'TS';
type prop = { name: string; age: number };
type other = { 'C++': string; Java: string };
type language = Record<lan, prop> & other;
const test1: language = {
JS: { name: 'javascript', age: 1 },
TS: { name: 'typescript', age: 2 },
'C++': 'C++语言',
Java: 'Java开发'
};
10. Pick
实现:
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }
作用:将T类型中的某些属性pick挑选出来,K即T类型中的属性名。
使用示例:
interface Person {
name: string;
age: number;
address: string;
}
type pick = Pick<Person, 'name'>; // pick类型: {name: string;}
type pick1 = Pick<Person, 'age' | 'address'>; // pick1 = { age: number; address: string; }
11. Exclude
实现:
type Exclude<T, U> = T extends U ? never : T
作用:对于联合类型,将T类型中的U类型剔除。
使用示例:
interface Person {
name: string;
age: number;
}
interface Student {
name: string;
age: number;
school: string;
}
// 作用于对象类型,T继承或包含U则返回never,否则返回T类型
type test = Exclude<Student, Person>; // type test = never
type test1 = Exclude<Person, Student>; // type test = Person
// 作用于联合类型:从T中剔除U类型
type language1 = 'JS' | 'TS';
type language2 = 'Java' | 'TS' | 'C++';
type lan = Exclude<language2, language1>; // type lan = "Java" | "C++"
type lan1 = Exclude<language1, language2>; //type lan1 = "JS"
对于联合类型,实际上Exclude比较:
type lan =
| ('Java' extends 'JS' | 'TS' ? never : 'Java')
| ('TS' extends 'JS' | 'TS' ? never : 'TS')
| ('C++' extends 'JS' | 'TS' ? never : 'C++');
// 最后的结果:type lan = "Java" | "C++"
12. Extract
实现(与Exclude相反):
type Extract<T, U> = T extends U ? T : never
作用:对于联合类型,将T类型中的U类型提取出来。
使用示例:
interface Person {
name: string;
age: number;
}
interface Student {
name: string;
age: number;
school: string;
}
// 作用于对象类型
type test = Extract<Student, Person>; // type test = Student
type test1 = Extract<Person, Student>; // type test = never
// 作用于联合类型:从T中提取U类型
type language1 = 'JS' | 'TS';
type language2 = 'Java' | 'TS' | 'C++';
type lan = Extract<language2, language1>; // type lan = "TS"
type lan1 = Extract<language1, language2>; //type lan1 = "TS"
13. Omit
实现:
type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
作用:将对象类型T中剔除K属性。
使用示例:
interface Student {
name: string;
age: number;
school: string;
}
// type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
type person = Omit<Student, 'school'>; // type person = { name: string; age: number; }
type person1 = Omit<Student, 'school' | 'age'>; // type person = { name: string; }
14. ReturnType
作用: ReturnType<T>
用于获取函数T的返回类型。
function add(num1: number, num2: number) {
return num1 + num2;
}
type test = ReturnType<typeof add>; // type test = number
type test1 = ReturnType<
() => {
name: string;
}
>; // type test1 = { name: string; }
15. Parameters
Parameters<T>
用于获取 获取函数类型T的参数类型
示例:
enum operatorEnum {
'+',
'-',
'*',
'/'
}
function add(
num1: number | string,
num2: number | string,
operator: operatorEnum = operatorEnum['+']
) {
if (typeof num1 === 'number' && typeof num2 === 'number') {
switch (operator) {
case operatorEnum['+']:
return num1 + num2;
case operatorEnum['-']:
return num1 - num2;
case operatorEnum['*']:
return num1 * num2;
case operatorEnum['/']:
return num1 / num2;
}
}
if (typeof num1 === 'string' && typeof num2 === 'string') {
return Number(num1) + Number(num2);
}
return '出错了';
}
type test = Parameters<typeof add>; // type test = [num1: string | number, num2: string | number, operator?: operatorEnum | undefined]
type test1 = Parameters<typeof add>[0]; // type test1 = string | number
type test2 = Parameters<typeof add>[1]; // type test2 = string | number
type test3 = Parameters<typeof add>[2]; // type test3 = operatorEnum | undefined
const demo1: test3 = '+'; // Error
const demo2: test3 = 10086; // Error
const demo3: test3 = operatorEnum['+'];
const test_01: test = [1, 2];
const test_02: test = ['1', 2, undefined];
const test_03: test = [2, '32', operatorEnum['*']];