Typescript实战:几个编写 ts 的实用技巧
编写 typescirpt 的几个实用技巧,掌握后可以让 ts 代码更加简洁
使用 unknown 代替 any
- 有些时候我们写一些方法时,不能确定传参是什么类型,经常会写成 any 类型,但是这样在某些情况下,在使用类型出错时,编译器没有给我们提示,比如下面的代码:
// 使用 any
function myFunction(fn: any) {
fn();
}
// Error: Argument of type '1' is not assignable to parameter of type 'any'.
myFunction(1);
// 使用 unknown
function myFunctionWithUnknown(fn: unknown) {
if (typeof fn === 'function') {
fn();
}
}
// No error
myFunctionWithUnknown(1);
- 从上面的代码中可以看到,当使用 any 作为参数类型时,我们在使用 fn 这个参数时不需要做任何的参数校验,哪怕我们传递个数字进去,但是按照方法来执行了,编译器也没有报错,但是,在运行的时候会发生错误!
- 而当我们把参数的类型换成 unknown 时,仍然可以传递任何类型,但是,在使用 fn 这个参数时,我们就需要做类型校验了,这样做可以避免一些运行时的错误。
使用 is 操作符
- 在某些时候,我们需要写一些有返回值的方法,比如下面的代码:
// use is operator
type Species = 'cat' | 'dog'
interface Pet {
species: Species
}
class Cat implements Pet {
public species: Species = 'cat'
public meow() {
console.log('Meow');
}
}
// use is operator
function isCat(pet: Pet): pet is Cat {
return pet.species === 'cat'
}
// return a boolean type
function isCatUseBoolean(pet: Pet): boolean {
return pet.species === 'cat'
}
const pet: Pet = new Cat()
if (isCatUseBoolean(pet)) {
// Error: Property 'meow' does not exist on type 'Pet'.
// pet.meow()
// need to cast to Cat
(pet as Cat).meow()
}
if (isCat(pet)) {
pet.meow()
}
- 上面这段代码关键在于判断是否是 Cat 类型,第一种是写个方法直接返回 boolean 值,第二种是通过 is 操作符来断言返回值的类型。
- 虽然这两种方法都可以做到判断传入的参数是否是 Cat 类型,但是,明显第二种通过 is 操作符的方式更简洁,不需要将参数再断言一次!
使用 satisfies 操作符
- 看下面这段 ts 代码:
// use satisfies operator
type RGB = [red: number, green: number, blue: number]
type Color = { value: RGB | string }
// const myColor: Color = { value: 'red' }
// Error: Type 'string' is not assignable to type '[number, number, number]'.
// myColor.value.toUpperCase()
const myColor = { value: 'red' } satisfies Color
myColor.value.toUpperCase()
- 定义了一个 Color 类型后将里面的 value 类型定义成 RGB 或者 string 类型,如果我们只是为 myColor 赋值成一个 string 字符串,那么我们期望在调用 myColor.value 时能够有字符串的方法提示,可是当调用 toUpperCase 方法时,编译器提示错误了,因为此时的 ts 编译器认为 value 是个联合类型,而数组是没有这个方法的。
- 接下来使用 satisfies 操作符,就可以避免这个问题了,当然如果把 value 设置成数组一样可以的
const myColor2 = {value: [255,255,255]} satisfies Color
myColor2.value.push(1)
使用 Utility Types
- Utility Types 是非常好用的 ts 内置类型,最常用的几个有 Partial,Omit, Record
Partial
- Partial 可以将一个传递进来的类型所有属性设置成可选的
interface Person {
name: string;
age: number;
}
// type PartialPerson = {
// name?: string | undefined;
// age?: number | undefined;
// }
type PartialPerson = Partial<Person>
- 还有个和 Partial 正好相反的 Required,可以将传递的类型里所有的属性变成必选的
// type RequiredPerson = {
// name: string;
// age: number;
// }
type RequiredPerson = Required<PartialPerson>
Omit
- Omit 接受两个参数,第一个是类型,第二个是去掉类型中的某几个属性
interface User {
id: number;
name: string;
email: string;
age: number;
}
// type OmitUser = {
// email: string;
// age: number;
// }
type OmitUser = Omit<User, 'id' | 'name'>
- Omit 也有个相反的方法,Pick,同样接受两个参数,第一个是类型,第二个是要留下哪几个属性
// type PickUser = {
// id: number;
// name: string;
// }
type PickUser = Pick<User, 'id' | 'name'>
Record
- Record 和上面几种不同,是用来约束一个类型的
type User = Record<string, string | number>
const user: User = {
name: 'John',
age: 20,
email: ''
}
type User2 = Record<'name' | 'age' | 'email', string | number>
const user2: User2 = {
name: 'John',
age: 20,
email: '',
}
- Record 的第一个参数用来约束 key 的类型,第二个参数用来约束 value 的类型
总结
以上这些就是使用 Typescript 开发时的一些小技巧,合理的运用可以让代码变得更加整洁,也更能体会出 Typescript 类型编程的含义。
转载自:https://juejin.cn/post/7225552757608120377