使用 TypeScript 也快一年了,本文主要分享一些工做经常使用的知识点技巧和注意点。前端
本文适合了解 TypeScript 或有实际使用过一段时间的小伙伴。git
若是对 TypeScript 基础知识不熟悉的能够看下我这篇文章:TypeScript 入门知识点总结github
用来给一个类型起个新名字api
type Props = TextProps
用于获取某种类型的全部键,其返回类型是联合类型。数组
interface Person { name: string age: number } type PersonKey = keyof Person // "name" | "age"
和逻辑 "||" 同样都是表示其类型为多个类型中的任意一个编辑器
好比当你在写Button 组件的时候:函数
export type ButtonSize = 'lg' | 'md' | 'sm'
将多个类型合并成一个类型工具
好比当你给一个新的 React 组件定义 Props 类型,须要用到其它若干属性集合post
type Props = TypographyProps & ColorProps & SpaceProps
主要做用是添加泛型约束单元测试
interface WithLength { length: number } // extends 来继承 function logger<T extends WithLength>(val: T) { console.log(val.length) } logger('hello') logger([1, 2, 3]) // logger(true) // error 没有length属性
typeof 是获取一个对象/实例的类型
interface Person { name: string age: number } const person1: Person = { name: 'monkey', age: 18 } const person2: typeof person1 = { name: 'jacky', age: 24 } // 经过编译
将某个类型里的属性所有变为可选项。
// 实现:所有变可选 type Partial<T> = { [P in keyof T]?: T[P] }
例子
interface Animal { canFly: boolean canSwim: boolean } // 变可选,能够只赋值部分属性 let animal: Partial<Animal> = { canFly: false, }
它接收一个泛型 T,用来把它的全部属性标记为只读类型
// 实现:所有变只读 type Readonly<T> = { readonly [P in keyof T]: T[P] }
interface Person { name: string age: number } let person: Readonly<Person> = { name: 'jacky', age: 24, } person.name = 'jack' // Cannot assign to 'name' because it is a read-only property.
将某个类型里的属性所有变为必选项。
// 实现:所有变必选 type Required<T> = { [P in keyof T]-?: T[P] }
interface Person { name?: string age?: number } // Property 'age' is missing in type '{ name: string; }' but required in type 'Required<Person>'. let person: Required<Person> = { name: 'jacky', // 没写 age 属性会提示错误 }
// 实现:K 中全部属性值转化为 T 类型 type Record<K extends keyof any, T> = { [P in K]: T }
Record 生成的类型具备类型 K 中存在的属性,值为类型 T
interface DatabaseInfo { id: string } type DataSource = 'user' | 'detail' | 'list' const x: Record<DataSource, DatabaseInfo> = { user: { id: '1' }, detail: { id: '2' }, list: { id: '3' }, }
// 实现:经过从Type中选择属性Keys的集合来构造类型 type Pick<T, K extends keyof T> = { [P in K]: T[P] }
用于提取接口的某几个属性
interface Animal { canFly: boolean canSwim: boolean } let person: Pick<Animal, 'canSwim'> = { canSwim: true, }
// 实现:若是 T 中的类型在 U 不存在,则返回,不然不返回 type Exclude<T, U> = T extends U ? never : T
将某个类型中属于另外一个的类型移除掉
interface Programmer { name: string age: number isWork: boolean isStudy: boolean } interface Student { name: string age: number isStudy: boolean } type ExcludeKeys = Exclude<keyof Programmer, keyof Student> // type ExcludeKeys = "isWork"
// 实现:去除类型 T 中包含 K 的键值对。 type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
做用与 Pick 相反,仍是直接看代码容易理解
interface Animal { canFly: boolean canSwim: boolean } let person1: Pick<Animal, 'canSwim'> = { canSwim: true, } let person2: Omit<Animal, 'canFly'> = { canSwim: true, }
// 实现:获取 T 类型(函数)对应的返回值类型 type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
获取函数返回值类型
function bar(x: string | number): string | number { return 'hello' } type FooType = ReturnType<typeof bar> // string | number
可选链操做符(?.
)容许读取位于链接对象链深处的属性的值,而没必要明确验证链中的每一个引用是否有效。若是遇到 null 或 undefined 就能够当即中止某些表达式的运行
这个在项目中比较经常使用,也很好用。
好比咱们数据都是从 api 返回的,假设有这么一组数据
// 请求 api 后才返回的数据 { data: { name: 'jacky', age: 24, address: { city: 'shenzhen', }, }, }
前端接收后拿来取值,apiRequestResult.data.address.city
,正常状况取获得值,若是后台 GG 没有返回 address 这个对象,那么就会报错:
TypeError: Cannot read property 'city' of undefined
这个时候须要前端处理了,好比最直接的就是
apiRequestResult && apiRequestResult.data && data.address && data.address.city
或者 lodash 库方法也能解决
import * as _ from 'lodash' const city = _.get(apiRequestResult, 'apiRequestResult.data.address.city', undefined)
项目中若是都是这么处理,未免太过于繁琐,特别对象不少的状况下,这时就可使用 TS 的可选链操做符,
apiRequestResult?.data?.address?.city
上述的代码会自动检查对象是否为 null 或 undefined,若是是的话就当即返回 undefined,这样就能够当即中止某些表达式的运行,从而不会报错。
当你在 React 中使用 ref 封装判断元素是否包含 XX 元素时,也会用到这个操做符
const isClickAway = !childRef.current?.contains?.(e.target)
空值合并操做符(??
)是一个逻辑操做符,当左侧的操做数为 null 或者 undefined 时,返回其右侧操做数,不然返回左侧操做数。
与 ||
操做符相似,可是 ||
是一个布尔逻辑运算符,左侧的操做数会被强制转换成布尔值用于求值。如0, '', NaN, null, undefined
都不会被返回。
const foo1 = null const foo2 = 12 const foo3 = 'hello' const res1 = foo1 ?? 'value' // 'value' const res2 = foo2 ?? 'value' // 12 const res3 = foo3 ?? 'value' // 'hello'
实际开发也能够混用?.
和??
const title = document.getElementById("title")?.textContent ?? "";
在上下文中当类型检查器没法判定类型时,这个运算符(!
)能够用在变量名或者函数名以后,用于断言操做对象是非 null 和非 undefined 类型。不过通常是不推荐使用的
这个适用于咱们已经很肯定知道不会返回空值
function print(name: string | undefined) { let myName: string = 'jacky' // Type 'string | undefined' is not assignable to type 'string'. // Type 'undefined' is not assignable to type 'string' myName = name }
要解决这个报错,能够加入判断
function print(name: string | undefined) { let myName: string = 'jacky' if (name) { myName = name } }
但这样写代码就显得冗长一点,这时你很是肯定不空,那就用非空断言运算符
function print(name: string | undefined) { let myName: string = 'jacky' myName = name! }
从而能够减小冗余的代码判断
避免 any,但检查过不了怎么搞?能够用 unknown(不可预先定义的类型)代替
给个不是很好的例子,由于下面例子是自己不对还强行修正错误。大概状况是有些未知数据的状况,须要强定义类型才能过,能够用(xx as unknown) as SomeType
的形式,固然这里只是例子,项目中类型检查逼不得已的状况下可能会使用
const metaData = { description: 'xxxx', } interface MetaType { desc: string } function handleMeta(data: MetaType): void {} // 这两种写法都会报错 // handleMeta(metaData) // handleMeta(metaData as MetaType) // 这里能够经过类型检查不报错,替代 any 的功能同时保留静态检查的能力, handleMeta((metaData as unknown) as MetaType)
通常用来约束对象和数组
interface StringObject { // key 的类型为 string, 表明对象 // 限制 value 的类型为 string [key: string]: string } let obj: StringObject = { name: 'jacky', age: 24, // 此行报错:Type 'number' is not assignable to type 'string'. }
interface StringArr { // key 的类型为 number, 表明数组 // 限制 value 的类型为 string [key: number]: string } let arr: StringArr = ['name', 'jacky']
经过 /** */
形式的注释让 TS 类型作标记提示,编辑器会有更好的提示
/** Animal common props */ interface Animal { /** like animal special skill */ feature: string } const cat: Animal = { feature: 'running', }
仍是那样,尽量用 unknown 来代替使用 any
若是大家项目禁用 any,那么 ts-ignore 是一个比 any 更有潜在影响代码质量的因素了。
禁用了但有些地方确实须要使用忽略的话,最好注释补多句理由。
好比有些函数单元测试文件,测试用例故意传入不正确的数据类型等,有时是逼不得已的报错。
// @ts-ignore intentionally for test