TS使用技巧
发表于更新于
宁波
IT前端TSTS使用技巧
观默使用类型声明而非类型断言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| type Circle = { kind: "circle"; radius: number }; type Rect = { kind: "rect"; width: number; height: number }; type Shape = Circle | Rect;
function isCircle(shape: Shape) { return shape.kind === "circle"; }
function isRect(shape: Shape) { return shape.kind === "rect"; }
const myShapes: Shape[] = getShapes();
const circles: Circle[] = myShapes.filter(isCircle);
|
更优雅的解决方案是将isCircle和isRect改为返回类型声明
这样TS在调用filter之后可以缩小类型范围:
1 2 3 4 5 6 7 8 9 10 11 12
| function isCircle(shape: Shape): shape is Circle { return shape.kind === "circle"; }
function isRect(shape: Shape): shape is Rect { return shape.kind === "rect"; }
const myShapes: Shape[] = getShapes();
const circles = myShapes.filter(isCircle);
|
推荐使用type替代interface
在 TypeScript 中,一个没有返回值(即没有调用 return 语句)的函数,其返回类型应当被标记为 void 而不是 undefined,即使它实际的值是 undefined。
1 2 3 4 5 6 7
| function foo(): void {}
function bar(): undefined { return; }
|
要想实现与入参关联的返回值类型,我们可以使用 TypeScript 提供的函数重载签名
1 2 3 4 5 6 7 8 9 10 11 12 13
| function func(foo: number, bar: true): string; function func(foo: number, bar?: false): number; function func(foo: number, bar?: boolean): string | number { if (bar) { return String(foo); } else { return foo * 599; } }
const res1 = func(599); const res2 = func(599, true); const res3 = func(599, false);
|
unknown 和 any 的一个主要差异体现在赋值给别的变量时,any 就像是 “我身化万千无处不在” ,所有类型都把它当自己人。而 unknown 就像是 “我虽然身化万千,但我坚信我在未来的某一刻会得到一个确定的类型” ,
用in进行类型推导
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| interface Foo { foo: string; fooOnly: boolean; shared: number; name: string; }
interface Bar { bar: string; barOnly: boolean; shared: number; age: number; }
function handle(input: Foo | Bar) { if ("bar" in input) { input.fooOnly; } else { input.barOnly; } }
|
常用工具类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type Partial<T> = { [P in keyof T]?: T[P]; };
type Required<T> = { [P in keyof T]-?: T[P]; };
type Readonly<T> = { readonly [P in keyof T]: T[P]; };
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
|
类型导入
1 2
| import { Foo } from "./foo"; import type { FooType } from "./foo";
|
TS 工具
ts-node与ts-node-dev:我们在环境搭建一节中已经介绍过,用于直接执行 .ts 文件。其中ts-node-dev基于ts-node和node-dev(类似于 nodemon)封装,能够实现监听文件改动并重新执行文件的能力。
tsc-watch:它类似于 ts-node-dev,主要功能也是监听文件变化然后重新执行,但tsc-watch的编译过程更明显,也需要自己执行编译后的文件。你也可以通过 onSuccess 与 onFailure 参数,来在编译过程成功与失效时执行不同的逻辑。
esno: antfu 的作品。核心能力同样是执行 .ts 文件,但底层是 ESBuild 而非 tsc,因此速度上会明显更快。
ts-error-translator,将 TS 报错翻译成更接地气的版本,并且会根据代码所在的上下文来详细说明报错原因,目前只有英文版本,中文版本感觉遥遥无期,因为 TS 的报错实在太多了……
typescript-json-schema:从 TypeScript 代码生成 JSON Schema,如以下代码:
1 2 3 4 5 6 7 8 9 10
| export interface Shape {
size: number; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { "$ref": "#/definitions/Shape", "$schema": "http://json-schema.org/draft-07/schema#", "definitions": { "Shape": { "properties": { "size": { "description": "The size of the shape.", "minimum": 0, "type": "integer" } }, "type": "object" } } }
|
json-schema-to-typescript,和上面那位反过来,从 JSON Schema 生成 TypeScript 代码:
type-fest,不用多介绍了,目前 star 最多下载量最高的工具类型库,Sindre Sorhus 的作品,同时也是个人认为最接地气的一个工具类型库。
utility-types,包含的类型较少,但这个库是我类型编程的启蒙课,我们此前对 FunctionKeys、RequiredKeys 等工具类型的实现就来自于这个库。
ts-essentials
type-zoo
ts-toolbelt,目前包含工具类型数量最多的一位,基本上能满足你的所有需要。
tsd,用于进行类型层面的单元测试,即验证工具类型计算结果是否是符合预期的类型,也是 Sindre Sorhus 的作品,同时 type-fest 中工具类型的单元测试就是基于它。
conditional-type-checks,类似于 tsd,也是用于对类型进行单元测试。
class-validator,TypeStack 的作品,基于装饰器来进行校验,我们会在后面的装饰器一节了解如何基于装饰器进行校验。