#5 控制联合类型的分布方式类型推断是TypeScript的特性;大多数时候,它默默地为你工作 。但是有时你可能对模棱两可的细微情况进行干预 。分布式条件类型就是其中一种情况 。
假设我们有一个ToArray辅助类,如果输入类型还不是数组类型,则返回数组类型:
type ToArray<T> = T extends Array<unknown> ? T: T[];
你认为以下类型会推断出什么?
type Foo = ToArray<string|number>;
答案是string[] | number[]? 。但这是模棱两可的 。为什么不是(string | number)[]呢?
默认情况下,当TypeScript遇到联合类型(此处为string | number?)的泛型参数(此处为T?)时,它会分布到每个组成部分中,这就是为什么会得到string[] | number[]?的原因 。你可以通过使用特殊语法并将T?包装在一对[]中来更改此行为,例如:
type ToArray<T> = [T] extends [Array<unknown>] ? T : T[];type Foo = ToArray<string | number>;
现在Foo?被推断为类型(string | number)[] 。
#6 使用详尽检查捕获在编译时未处理的情况在switch?语句中使用enum枚举时,一个好习惯是在没有匹配到合适值的情况下主动抛错,而不是像在其他编程语言中那样默默地忽略它们:
function getArea(shape: Shape) {switch (shape.kind) {case 'circle':return Math.PI * shape.radius ** 2;case 'rect':return shape.width * shape.height;default:throw new Error('Unknown shape kind');}}
通过使用never类型,静态类型检查就可以更早地查找到错误:
function getArea(shape: Shape) {switch (shape.kind) {case 'circle':return Math.PI * shape.radius ** 2;case 'rect':return shape.width * shape.height;default:// you'll get a type-checking error below// if any shape.kind is not handled aboveconst _exhaustiveCheck: never = shape;throw new Error('Unknown shape kind');}}
有了这个,在添加新的shape?种类时,就不可能忘记更新getArea函数 。
该技术背后的基本原理是,除了never?之外,不能为never?类型分配任何内容 。如果shape.kind?的所有备选项都被case?语句用尽,那么达到default?的唯一可能类型是never?;但是,如果未涵盖所有备选项,则将泄漏到default分支并导致无效分配 。
#7 宁可使用type而不是interface在TypeScript中,type和interface?是两种非常相似的数据结构,都可以用来构造复杂的对象的 。虽然可能有争议,但我的建议是在大多数情况下始终使用type,仅在满足以下任一条件时才使用interface:
- 想利用interface的合并功能 。
- 有涉及类/接口层次结构的OO样式代码 。
#8 只要合适宁可使用元组而不是数组对象类型是构造结构化数据的常用方法,但有时你可能希望使用更简洁的表示形式,而改用简单的数组 。例如,Circle可以定义为:
type Circle = (string | number)[];const circle: Circle = ['circle', 1.0];// [kind, radius]
但是这种构造是松散的,如果创建类似['circle', '1.0']的内容很容易出错 。我们可以通过使用元组来使其更严格:type Circle = [string, number];// you'll get an error belowconst circle: Circle = ['circle', '1.0'];
使用元组的一个很好的例子是React中的useState 。const [name, setName] = useState('');
既紧凑又类型安全 。#9 控制推断类型的通用性或特殊性TypeScript在进行类型推断时使用合理的默认行为,旨在使常见情况下的代码编写变得容易(因此类型不需要显式注释) 。有几种方法可以调整其行为 。
- 使用const缩小到最具体的类型
let foo = { name: 'foo' }; // typed: { name: string }let Bar = { name: 'bar' } as const; // typed: { name: 'bar' }let a = [1, 2]; // typed: number[]let b = [1, 2] as const; // typed: [1, 2]// typed { kind: 'circle; radius: number }let circle = { kind: 'circle' as const, radius: 1.0 };// the following won't work if circle wasn't initialized// with the const keywordlet shape: { kind: 'circle' | 'rect' } = circle;
- 使用satisfies来检查类型,而不影响推断的类型
type NamedCircle = {radius: number;name?: string;};const circle: NamedCircle = { radius: 1.0, name: 'yeah' };// error because circle.name can be undefinedconsole.log(circle.name.length);
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 徐怀钰|《浪姐4》一公录完,谢娜组全员晋级,徐怀钰跳舞放炮,5人被淘汰
- GPT冲击了哪些科技从业者的饭碗?程序员较“惨” 创意类、知识类从业者受益
- 程序员|未来十年,除了程序员,谁都不好混
- “流动型程序员”指的是什么类型的程序员?
- GPT-4解放程序员!GitHub推出Copilot X,动动嘴就能写代码
- 程序员|作为一个程序员为什么今年的工作感觉更难找了呢
- 程序员是什么专业(程序员要考什么证)
- 程序员|职业生涯:纵横螺旋多元通道进化,由内而外综合能力升级
- 程序员用什么键盘(程序员键盘推荐)
- 国足晋级12强赛怎么进世界杯 12强赛怎么晋级世界杯