前言
类型编程在项目中的重要性不言而喻,本文将总结一些 Typescript 的常用特性,帮助大家熟悉掌握 Typescript 的使用。
一、高级类型
交叉类型
交叉类型就是通过 & 符号,将多个类型合并为一个类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
interface I1 { name: string; } interface I2 { age: number; } type T3 = I1 & I2 const a: T3 = { name: 'tj', age: 11, } |
联合类型
联合类型就是通过 | 符号,表示一个值可以是几种类型之一。
1 |
const a: string | number = 1 |
字符串字面量类型
字符串字面量类型就是使用一个字符串类型作为变量的类型。
1 |
const a: 'number' = 'number' |
数字字面量类型
数字字面量类型就是使用一个数字作为变量的类型。
1 |
const a: 1 = 1 |
布尔字面量类型
数字字面量类型就是使用一个布尔值作为变量的类型。
1 |
const a: true = true |
字符串模板类型
字符串模板类型就是通过 ES6 的模板字符串语法,对类型进行约束。
1 2 |
type https = `https://${string}` const a:https = `https://jd.com` |
二、操作符
keyof
keyof 用于获取某种类型的所有键,其返回值是联合类型。
1 2 3 4 5 |
// const a: 'name' | 'age' = 'name' const a: keyof { name: string, age: number } = 'name' |
typeof
typeof 用于获取对象或者函数的结构类型。
1 2 3 4 5 6 7 8 9 10 11 |
const a2 = { name: 'tj', } type T1 = typeof a2 // {name: string} function fn1(x: number): number { return x * 10 } type T2 = typeof fn1 // (x: number) => number |
in
in 用于遍历联合类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const obj = { name: 'tj', age: 11 } type T5 = { [P in keyof typeof obj]: any } /* { name: any, age: any } */ |
T[K]
T[K] 用于访问索引,得到索引对应的值的联合类型。
1 2 3 4 5 6 |
interface I3 { name: string, age: number } type T6 = I3[keyof I3] // string | number |
三、运算符
非空断言运算符
非空断言运算符 ! 用于强调元素是非 null 非 undefined,告诉 Typescript 该属性会被明确的赋值。
1 2 3 4 5 6 7 |
function Demo(): JSX.Element () { const divRef = useRef<HTMLDivElement>() useEffect(() => { divRef.current!.scrollIntoView() // 断言divRef.current 是非 null 非 undefined }, []) return <div ref={divRef}>divDemo</div> } |
可选链运算符
可选链运算符 ?. 用于判断左侧表达式的值是否是 null 或 undefined,如果是将停止表达式运行。
1 |
const a = b?.a |
空值合并运算符
空值合并运算符 ?? 用于判断左侧表达式的值是否是 null 或 undefined,如果不是返回右侧的值。
1 |
const a = b ?? 10 |
数字分隔符
数字分隔符 _ 用于对长数字进行分割,便于数字的阅读,编译结果将会自动去除 _。
1 |
const num: number = 1_111_111_111 |
四、类型别名
类型别名用来给一个类型起个新名字。
1 2 3 4 |
type Message = string | string[] let greet = (message: Message) => { // ... } |
五、类型断言
类型断言就是告诉浏览器我非常确定的类型。
1 2 3 4 5 6 7 |
// 尖括号 语法 let someValue: any = "this is a string"; let strLength: number = (<string>someValue).length; // as 语法 let someValue: any = "this is a string"; let strLength: number = (someValue as string).length; |
七、类型守卫
类型守卫就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
interface A { name: string; age: number; } interface B { sex: string; home: string; } function doSomething(person: A | B): void { if (person.name) { // Error // ... } } // 使用 in 类型守卫 function doSomething(person: A | B): void { if ('name' in person) { // OK // ... } } // 使用 typeof 类型守卫 function A(a: string | number): string | number { if (typeof a === 'string') { // OK return a + '' } if (typeof a === 'number') { // OK return a + 2 } return '' } // instanceof类型守卫 class Foo {} class Bar {} function test(input: Foo | Bar) { if (input instanceof Foo) { // 这里 input 的类型「收紧」为 Foo } else { // 这里 input 的类型「收紧」为 Bar } } |
八、泛型
1. 泛型介绍
泛型就是通过给类型传参,得到一个更加通用的类型,就像给函数传参一样。
如下我们得到一个通用的泛型类型 T1,通过传参,可以得到 T2 类型 string[]、T3 类型 number[]:
1 2 3 4 5 |
type T1<T> = T[] type T2 = T1<string> // string[] type T3 = T1<number> // number[] |
如上 T 是变量,我们可以用任意其他变量名代替他。
1 2 3 4 5 |
type T4<A> = A[] type T5 = T4<string> // string[] type T6 = T4<number> // number[] |
2. 命名规范
在 Typescript 泛型变量的命名规范中,默认了 4 种常见的泛型变量名,为提高可读性,不建议改为其他的变量名来定义。
- T:表示第一个参数
- K: 表示对象的键类型
- V:表示对象的值类型
- E:表示元素类型
3. 泛型接口
泛型接口和上述示例类似,为接口类型传参:
1 2 3 4 5 6 |
interface I1<T, U> { name: T; age: U; } type I2 = I1<string, number> |
4. 泛型约束(extends 操作符)
有时候,我们需要对泛型参数进行约束,限制每个变量的类型。Typescript 通过 extends 实现类型约束。
泛型约束语法如下:
1 |
泛型名 extends 类型 |
通过 T extends Length 约束了 T 的类型,必须是包含 length 属性,且 length 的类型必须是 number。
1 2 3 4 5 6 7 |
interface Length { length: number } function fn1<T extends Length>(arg: T): number{ return arg.length } |
通过 extends 约束了 K 必须是 T 的 key。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } let tsInfo = { name: "Typescript", supersetOf: "Javascript", difficulty: Difficulty.Intermediate } let difficulty: Difficulty = getProperty(tsInfo, 'difficulty'); // OK let supersetOf: string = getProperty(tsInfo, 'superset_of'); // Error |
5. 泛型参数默认值
泛型参数默认值,和函数参数默认值一样,在没有传参时,给定默认值。
1 2 3 4 5 6 |
interface I4<T = string> { name: T; } const S1: I4 = { name: '123' } // 默认 name: string类型 const S2: I4<number> = { name: 123 } |
6. 泛型条件
条件类型和 Js 的条件判断意思一样,都是指如果满足条件,就 xx,否则 xx。
条件类型表达式:
1 |
T extends U ? X : Y |
如果 T 能够赋值给 U,那么类型是 X,否则类型是 Y。
1 2 3 |
type T1<T> = T extends string ? 'string' : 'number' type T2 = T1<string> // 'string' type T3 = T1<number> // 'number |
7. 泛型推断(infer 操作符)
泛型推断的关键字是 infer,语法是 infer 类型
。
一般是和泛型条件结合使用,结合实际例子理解:
如果泛型参数 T 能赋值给类型 {t: infer Test}
,那么类型是推断类型 Test,否则类型是 string。
1 |
type Foo<T> = T extends {t: infer Test} ? Test : string |
- 泛型参数 number 没有 t 属性,所以类型是 string
1 |
type One = Foo<number> // string |
- 泛型参数的 t 属性是 boolean,所以类型是推断类型 boolean
1 |
type Two = Foo<{ t: boolean }> // boolean |
泛型参数的 t 属性是 () => void,所以类型是推断类型 () => void
1 |
type Three = Foo<{ a: number, t: () => void }> // () => void |
8. 泛型工具类型
映射类型
映射类型,它是一种泛型类型,可用于把原有的对象类型映射成新的对象类型。
常见的映射类型语法:
1 2 3 4 5 6 |
{ [ P in K ] : T } { [ P in K ] ?: T } { [ P in K ] -?: T } { readonly [ P in K ] : T } { readonly [ P in K ] ?: T } { -readonly [ P in K ] ?: T } |
举例说明,通过映射类型将所有属性变为可选:
1 2 3 |
type Partial<T> = { [P in keyof T]?: T[P] } |
Partial
Typescript 已将一些常用的映射类型进行封装,如 Partial 就是用于将泛型的全部属性变为可选。
1 2 3 4 5 6 7 8 9 |
type Partial<T> = { [P in keyof T]?: T[P] } type T1 = Partial<{ name: string, }> const a: T1 = {} // 没有name属性也不会报错 |
Required
Required 将泛型的所有属性变为必选。
1 2 3 4 5 6 7 8 9 |
type Required<T> = { [P in keyof T]-?: T[P] } type T2 = Required<{ name?: string, }> const b: T2 = {} // ts报错,类型 "{}" 中缺少属性 "name",但类型 "Required<{ name?: string | undefined; }>" 中需要该属性。ts(2741) |
语法-?,是把?可选属性减去的意思
Readonly
Readonly 将泛型的所有属性变为只读。
1 2 3 4 5 6 7 8 9 |
type T3 = Readonly<{ name: string, }> const c: T3 = { name: 'tj', } c.name = 'tj1' // ts 报错,无法分配到 "name" ,因为它是只读属性。ts(2540) |
Pick
Pick 从类型中选择一下属性,生成一个新类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
type Pick<T, K extends keyof T> = { [P in K]: T[P] } type T4 = Pick< { name: string, age: number, }, 'name' > /* 这是一个新类型,T4={name: string} */ const d: T4 = { name: 'tj', } |
Record
Record 将 key 和 value 转化为 T 类型。
1 2 3 4 5 6 7 8 9 10 11 |
type Record<K extends keyof any, T> = { [key in K]: T } const e: Record<string, string> = { name: 'tj', } const f: Record<string, number> = { age: 11, } |
keyof any 对应的类型为 number | string | symbol,是可以做对象键的类型集合。
ReturnType
ReturnType 获取 T 类型对应的返回值类型。
1 |
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any |
Exclude
Exclude 将某个类型中属于另一个的类型移除掉。
1 2 3 4 5 |
type Exclude<T, U> = T extends U ? never : T; 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 |
Extract
Extract 从 T 中提取出 U。
1 2 3 4 |
type Extract<T, U> = T extends U ? T : never; type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a" type T1 = Extract<string | number | (() => void), Function>; // () =>void |
Omit
Omit 使用 T 类型中除了 K 类型的所有属性,来构造一个新的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; interface Todo { title: string; completed: boolean; description: string; } type TodoPreview = Omit<Todo, "description">; /* { title: string; completed: boolean; } */ |
NonNullable
NonNullable 用来过滤类型中的 null 及 undefined 类型。
1 2 3 4 |
type NonNullable<T> = T extends null | undefined ? never : T; type T0 = NonNullable<string | number | undefined>; // string | number type T1 = NonNullable<string[] | null | undefined>; // string[] |
Parameters
Parameters 用于获得函数的参数类型组成的元组类型。
1 2 3 4 5 6 7 |
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never; type A = Parameters<() =>void>; // [] type B = Parameters<typeofArray.isArray>; // [any] type C = Parameters<typeofparseInt>; // [string, (number | undefined)?] type D = Parameters<typeofMath.max>; // number[] |
参考资料
- 2021 typescript 史上最强学习入门文章(2w 字)
- TypeScript 高级用法
- 重学 TypeScript
- Typescript 官网
- 一文读懂 TypeScript 泛型及应用( 7.8K 字)
form:https://juejin.cn/post/7102970819231350791