共计 5396 个字符,预计需要花费 14 分钟才能阅读完成。
前言
应用 TypeScript 也快一年了,本文次要分享一些工作罕用的知识点技巧和留神点。
本文适宜理解 TypeScript 或有理论应用过一段时间的小伙伴。
如果对 TypeScript 基础知识不相熟的能够看下我这篇文章:TypeScript 入门知识点总结
操作符
类型别名
用来给一个类型起个新名字
type Props = TextProps
keyof
用于获取某种类型的所有键,其返回类型是联结类型。
interface Person { | |
name: string | |
age: number | |
} | |
type PersonKey = keyof Person // "name" | "age" |
联结类型
和逻辑 “||” 一样都是示意其类型为多个类型中的任意一个
比方当你在写 Button 组件的时候:
export type ButtonSize = 'lg' | 'md' | 'sm'
穿插类型
将多个类型合并成一个类型
比方当你给一个新的 React 组件定义 Props 类型,须要用到其它若干属性汇合
type Props = TypographyProps & ColorProps & SpaceProps
extends
次要作用是增加泛型束缚
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
typeof 是获取一个对象 / 实例的类型
interface Person { | |
name: string | |
age: number | |
} | |
const person1: Person = {name: 'monkey', age: 18} | |
const person2: typeof person1 = {name: 'jacky', age: 24} // 通过编译 |
泛型工具类型
Partial
将某个类型里的属性全副变为可选项。
// 实现:全副变可选 | |
type Partial<T> = {[P in keyof T]?: T[P] | |
} |
例子
interface Animal { | |
canFly: boolean | |
canSwim: boolean | |
} | |
// 变可选,能够只赋值局部属性 | |
let animal: Partial<Animal> = {canFly: false,} |
ReadOnly
它接管一个泛型 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. |
Required
将某个类型里的属性全副变为必选项。
// 实现:全副变必选 | |
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 属性会提醒谬误 | |
} |
Record
// 实现: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'}, | |
} |
Pick
// 实现:通过从 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,} |
Exclude
// 实现:如果 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" |
Omit
// 实现:去除类型 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,} |
ReturnType
// 实现:获取 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
防止 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',} |
ts-ignore
还是那样,尽可能用 unknown 来代替应用 any
如果你们我的项目禁用 any,那么 ts-ignore 是一个比 any 更有潜在影响代码品质的因素了。
禁用了但有些中央的确须要应用疏忽的话,最好正文补多句理由。
比方有些函数单元测试文件,测试用例成心传入不正确的数据类型等,有时是逼不得已的报错。
// @ts-ignore intentionally for test
- 集体技术博文 Github 仓库
感觉不错的话欢送 star,给我一点激励持续写作吧~