TypeScript笔记
/// <reference types="..." />
写在 d.ts 最顶端, 相当于导入指定 library 的声明文件
// index.d.ts/// <reference types="@reduxjs/toolkit/query/react"/>
没有写 reference 的时候, 输入 createApi 也不会出现自动补全和自动导入 因为@reduxjs/toolkit/query/react 里面虽然存在 d.ts 但是没有在 library 的 package.json 里面指定的 dist/index.d.ts 里面导出, 所以 ts 的编译器无法自动识别@reduxjs/toolkit/query/react 的 d.ts 文件 写了 reference 之后 ts 就知道要识别指定 library 的声明文件了, 所以就可以自动补全和自动导入了
// 业务逻辑ts文件import { createApi } from '@reduxjs/toolkit/dist/query/react'export const userApi = createApi
?
在 key 后面的?
表示此类型可为undefined
, 在类型前面的?
表示此类型可为null
interface Person { // name的类型为string name: string // sex的类型为'm'或者'f'或者undefined sex?: 'm' | 'f' // address的类型为string或者null address: ?string // tel的类型为number或者undefined或者null tel?: ?number}
在 key 后面的-?
表示删除类型 key 后面的的?
type NonUndefined<T> = { [key in keyof T]-?: T[key]}interface Mail { gmail?: string outlook: string | undefined yahoo?: string | undefined iCould: string}type NonUndefinedMail = NonUndefined<Mail>// 生成的类型如下type NonUndefinedMail = { // 单纯的key后面加问号的undefined被去掉了 gmail: string // 联合类型的undefined没有被去掉 outlook: string | undefined // key后面加问号并且联合类型中有undefined的也被去掉了 yahoo: string // 本来就是string的还是string iCould: string}
说明类型后面的?和联合类型的undefined
还是有区别的, 联合类型的 undefined 是说这个属性的值可以是undefined
, 但是 key 如果没有加问号的话这个 key 还是必须存在的; 而 key 后面的问号既可以说这个属性是可选的, 又可以说明这个属性的值可以是undefined
, 并且如果这个属性的类型被定义了联合类型的undefined
的话, key 后面的?
会将联合类型的undefined
的效果覆盖掉
所以使用的时候最好还是不要一块用, 还是要分工明确, 可选属性不管想不想给类型加上undefined
都只用 key 后面加?
; 不想让属性变成可选, 只是想加上undefined
, 那就用联合类型, 不要图省事只给 Key 后面加?
接口
- 接口可以定义很多次,所有定义的接口最终被自动合并为单个接口
interface Person { name: string}interface Person { age: number}const person: Person = { name: 'Tom', age: 20 }
- 为什么尽量用接口呢,因为接口更符合 js 的对象的工作方式
// 接口扩展interface Sister { sex: number}// 报错 因为 'string'无法分配给'number' 所以接口无法扩展interface SisterAn extends Sister { sex: string}// 交叉类型type Sister1 = { sex: number}type Sister2 = { sex: string}// 不报错 因为SisterAn的类型为 'number & string' 也就是'never'type SisterAn = Sister1 & Sister2
属性名
- 可以用索引签名添加指定类型的 key
type Person = { name: string age: number // 任意字符串都可以作为key value的类型为any [key: string]: any}
- 但是如果用了索引签名的话,所有确定属性都必须是索引签名定义的类型的子集
type Person = { // 报错(ts2411) 'name'的类型‘string’无法分配给索引类型的类型'number' name: string age: number [key: string]: number}
- 如果想让确定属性和可选属性(索引签名)互不冲突的话,可以使用交叉类型&(只支持类型别名
type
)
type Person = { name: string age: number} & { [key: string]: number}// 这样做虽然可以用于从某些地方获取的js对象, 但是无法用这个创建一个新对象const fn = (person: Person) => { // 比如这样做会正常报错 告诉你test只可能是一个number if (person.test === 'test') { return person.name }}// 但是这样就不行 会告诉你'{ name: string; age: number; }'无法分配给'{ [key: string]: number; }'const personTom: Person = { name: 'Tom', age: 1,}// 当然不是不可以解决这个问题 实在不行还可以断言是吧(const personJerry = { name: 'Jerry', age: 2,} as Person
in
- 可以用来遍历枚举类型
type Keys = 'a' | 'b' | 'c'// 这里必须用type定义 用interface定义的话会导致报错(ts7061)type Obj = { [key in Keys]: string}// 等于type Obj = { a: string b: string c: string}
常用工具类
Partial<T>
将泛型中全部属性变为可选
type Partial<T> = { [key in keyof T]?: T[P]}
Record<K, T>
快速定义对象
type Record<K extends keyof any, T> = { [key in K]: T}
Pick<T, K>
将 T 类型中的 K 键列表提取出来,生成新的子键值对类型
type Pick<T, K extends keyof T> = { [P in K]: T[P]}
Extract<T, U>
从 T 中提取出 U
type Extract<T, U> = T extends U ? T : never
Exclude<T, U>
去除联合类型 T 中的 U 类型
type Exclude<T, U> = T extends U ? never : T
Omit<T, K>
去除类型 T 中包含 K 的键值对
type Omit = Pick<T, Exclude<keyof T, K>>
ReturnType<T>
获取 T 类型(函数)对应的返回值类型
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any// 等于type ReturnType<T extends func> = T extends () => infer R ? R : any
Required<T>
将类型 T 中所有的属性变为必选项
type Required<T> = { [P in keyof T]-?: T[P]}
NonNullable<T>
过滤掉联合类型中的 null 和 undefined 类型
type NonNullable<T> = T extends null | undefined ? never : T
Parameters<T>
获取函数的全部参数类型,以元组类型返回
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
模式匹配
Typescript 类型的模式匹配是通过 extends 对类型参数做匹配,结果保存到通过 infer 声明的局部类型变量里,如果匹配就能从该局部变量里拿到提取出的类型
- 数组
type Pop<T extends unknown[]> = T extends [...infer Rest, infer R] ? Rest : never// res = [1, 2]type res = Pop<[1, 2, 'a']>
- 递归实现字符串去行尾空格以及换行符
type TrimRight<Str extends string> = Str extends `${infer Rest}${' ' | '\t' | '\n'}` ? TrimRight<Rest> : Str// trimStr = 'abctype trimStr = TrimRight<`abc\n \n`>
- 替换字符串中的一部分(replace)
type Replace< Str extends string, From extends string, To extends string,> = Str extends `${infer Left}${From}${infer Right}` ? `${Left}${To}${Right}` : Str// res = 'My father is Tom'type res = Replace<'My name is Tom', 'name', 'father'>
- 获取函数的参数类型(返回数组)
type GetParams<Fn extends Function> = Fn extends (...prams: infer Params) => void ? Params : never// res = [a: number, b: boolean]type res = GetParams<(a: number, b: boolean) => string>