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['*']];
posted @ 2023-04-19 09:36  青柠i  阅读(33)  评论(0编辑  收藏  举报