TypeScript进阶实战

TypeScript进阶实战

Tags
TypeScript
CreatedTime
Aug 18, 2022 10:59 AM
Slug
2020-10-20-advanced-data-strcture
UpdatedTime
Last updated August 18, 2022

TypeScript 高级数据类型

Dictionary 和 NumericDictionary

Dictionary:一个对象,键是 string 类型。
NumericDictionary:一个对象,键是 number 类型。
注意:要手动实现下,通过「索引签名」+「范型」。
定义:
interface Dictionary<T = any> { [index: string]: T; } interface NumericDictionary<T = any> { [index: number]: T; }
使用起来:
const data2: Dictionary<number> = { a: 3, b: 4, };

Record

ts 原生支持,看下它的定义:
type Record<K extends string | number | symbol, T> = { [P in K]: T };
简单来说,对于类型 Record<KEY_TYPE, VALUE_TYPE> 声明的对象,键的类型就是 KEY_TYPE,值的类型是 VALUE_TYPE。
它和直接用 interface 有啥区别? 一般 Record 可以用来做字段扩展、将值的类型转化为指定的 VALUE_TYPE、将键的类型转化为指定的 KEY_TYPE。
例子:
interface Person { name: string age: number } type PersonType = Record<'location' | keyof Person, number> // type PersonType = { // location: number; // name: number; // age: number; }

Pick

ts 原生支持:
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
作用:将某个类型指定键挑出来,组成新的类型。
例如:
interface Student { name: string; age: number; score: string; } type Person = Pick<Student, "age" | "name">; // type Person = { // age: number; // name: string; // }

Exclude

ts 原生支持:
// 提取T包含在U中的类型 type Extract<T, U> = T extends U ? T : never;
作用:将联合类型中的指定类型去掉。
例如:
type NUMBERS = 0 | 1 | 2 | "a" | "b"; type CORRECT_NUMBERS = Exclude<NUMBERS, "a" | "b">; // CORRECT_NUMBERS 类型是 0 | 1 | 2

Extract

作用:将公有属性提取出来。
例如:
type NUMBERS = 0 | 1 | 2 | "a" | "b"; type PARTIAL_NUMBERS = Extract<NUMBERS, 0 | 1>; // PARTIAL_NUMBERS 类型是 0 | 1

函数相关-Parameters

作用:获取函数参数类型。
例如:
function getPerson(name: string, value?: number): any {} type F = Parameters<typeof getPerson>; // F 类型是 [name: string, value?: number]

函数相关-ReturnType

作用:获取函数返回类型。
例如:
function getPerson(name: string, value?: number): any {} type F = ReturnType<typeof getPerson>; // F 类型是 any

TypeScript 高级操作符

const 断言

特点:
  • 字面量(数组、接口)类型变为 readonly
  • 字面量类型不能被扩展
举例:
不使用 const 断言:
const CONFIG_KEYS = ["name", "school", "country"]; type KEY_TYPES = typeof CONFIG_KEYS; // KEY_TYPES 是 string[] type KEY_TYPE = typeof CONFIG_KEYS[number]; // KEY_TYPE 是 string
使用 const 断言,ts 推断更精确:
const CONST_CONFIG_KEYS = ["name", "school", "country"] as const; type COPNST_KEY_TYPES = typeof CONST_CONFIG_KEYS; // COPNST_KEY_TYPES 是 readonly ["name", "school", "country"] type CONST_KEY_TYPE = typeof CONST_CONFIG_KEYS[number]; // CONST_KEY_TYPE 是 "name" | "school" | "country"

typoef 和 type

typeof 在 js 中,可以获得对象类型.
在 ts 中,能够配合 type 关键词,将对象类型保存在类型字面量中。
const CONFIG_KEYS = ["name", "school", "country"]; console.log(typeof CONFIG_KEYS); // 输出;object type KEY_TYPES = typeof CONFIG_KEYS; // KEY_TYPES 是 string[]

keyof

keyof 用来获取某种类型的所有键,注意不是某个变量。
例子:假设声明了一个 json 对象,然后想获取它的所有键的类型应该怎么办?
// 利用 const 断言,告诉解释器不能被扩展 const VARS = { NODE_ENV: "production", AUTHOR: "xintan", } as const; // 再获取它的类型 type VARS_TYPE = typeof VARS; // 最后获取类型上的key的类型 type VARS_KEYS_TYPE = keyof VARS_TYPE; // "NODE_ENV" | "AUTHOR"
再来一个用例,比如断言Reflect.ownKeys()的返回结果的类型:
const obj = { name: "12yuanxin3", age: 22, } as const; const objKeys = <(keyof typeof obj)[]>Reflect.ownKeys(obj); // objKeys 类型是: ("name" | "age")[]

extends

基础的用法:可以用于 class 继承、interface 继承。

用法 1: T extends U ? X : Y

用法:T extends U ? X : Y。这个比较难理解,而且很常见。其实就是:如果 T 包含的类型是 U 包含的类型的 '子集',那么取结果 X,否则取结果 Y
举例:
type Diff<T, U> = T extends U ? never : T; // 找出T和U的差集 type Filter<T, U> = T extends U ? T : never; // 找出T和U的交集

用法 2: 范型约束

用法:<T extends Lengthwise>
举例:
interface Lengthwise { length: number; } function loggingIdentity<T extends Lengthwise>(arg: T): T { console.log(arg.length); return arg; }
如果写成 function loggingIdentity<T>(arg: T): T,就会报错:T 上不存在 length 属性。正如所见,约束范型,传入的 arg 必须有 length 属性。

infer

作用:针对 ts 推断的类型,声明一个字面量,在extends语句中使用。
举例:
// 获得函数的返回类型 // 首先,范型约束了 T 是传入的函数的类型 // 然后,利用 extends 扩展表达式 + infer,将函数返回类型设置为R // 最后,因为对T进行了约束,所以表达式一定是 true,也就是一定返回 R // 我的理解:第2和3步骤就是为了拿到函数返回类型 type ReturnType<T extends (...args: any) => any> = T extends ( ...args: any ) => infer R ? R : any;
例子中的infer R就是传入参数(某个函数)的返回类型,在这个表达式中,函数的返回类型被R变量代替。
:::warning infer 使用的位置 infer 声明的这个变量只能在 true 分支中使用 :::

反解 Promise

理解了 ReturnType,我写出了一个反解 Promise的 typescript 扩展类型:
type UnPromiseType<T extends (...args: any) => any> = T extends ( ...args: any ) => Promise<infer U> ? U : never;
使用效果:
async function main(): Promise<string> { return "abc"; } type returnTypeWithoutPromise = UnPromiseType<typeof main>; // returnTypeWithoutPromise 是 string

参考链接

  • 《深入理解 TypeScript》