第四章:类型定义
条款28:倾向选择代表有效状态的类型
什么是有效状态?(个人理解)
状态是一个变量对应一种状态的。多个变量组合构成一种状态不能说成一种有效状态。
- request请求中我们常常设置loading和error状态,显示不同的ui
// 如果loading,error有遗漏的控制逻辑,那么就会产生bug
// loading 和error代码逻辑阅读有一点点成本
async function getInfo() {
let loading = true
let error = false
try {
await APIs.getInfo()
} catch(e) {
error = true
}
loading = false
}
function handleState() {
if (error) { throw Error('request error')}
if (loading) { // render loading }
// render data
}
// 取而代之使用一个有效状态控制是更加清晰的
async function getInfo() {
request.state = 'loading'
try {
await APIs.getInfo()
request.state = 'success'
} catch(e) {
request.state = 'error'
}
}
举了一个血腥飞机空难的例子,原因是控制杆的角度,由左右杆子共同控制。但是左右杆子控制逻辑在一定场景下产生了漏洞。
条款29:(函数入参和结果)宽进严出
条款30:不要在文档中重复信息(注释和ts类型推导)
条款31:空值处理
- 避免一个值是否为null和另一个值是否为null有隐式关系。
- 如果有同步null的关系,可以声明一个大对象(完全null、完全非null更好判断)
const getMinMax = (arr: number[]) => {
let min: number, max: number
arr.forEach(item => {
// wrong
if (!min) {
min = item
max = item
} else {
min = Math.min(min, item)
min = Math.min(max, item)
}
})
}
条款32:优选接口的联合,而不是联合的接口(组合类型制定更精准的类型
// 标签类型也有类似的思想
interface A { person: Student, address: StudentAddress}
interface B { person: Teacher, address: TeacherAddress}
// good
type C = A | B
// bad: 使用的时候可能产生了Student + TeacherAddress这类组合
type C = {
person: Student | Teacher,
address: StudentAddress | TeacherAddress
}
- 当多个属性是一起存在或者不存在时,包装一个大对象,大对象设置为可选。 这也像把null值推到边界(佩服作者的联想能力
条款33: 收窄类型
比如string,有时可以定义为枚举值,有时是object的key
使用泛型收窄 也是一种手段
条款34:不正确的类型比没有类型更加可怕
条款35:从接口定义中生成类型
条款36:准确的命名类型
条款37:限制结构类型的参数
第五章:和any一起工作
- 收缩any的作用范围
// good
const res = returningFoo() // res is Foo
receivePoo(res as any) param is expected Poo
res is Foo
// bad: res is any
const res: any = returningFoo() // res is Foo
receivePoo(res)
res is any
-
any的演变
有时候变量没有被初始化(推断为any),会因为使用的过程,能演变为更加精确的类型。 如果变量被显式定义为any,则不会有演变的过程。 但是演变规律,书也没给出很具体的规则,所以看过就算了吧 -
any会不断向下渗透传递,使用unknown就能避免这个问题
-
改变全局变量的类型(Document, Window)
第六章: 类型声明和@types
- 警惕使用泛型来函数重载时的陷阱
- T的类型是什么,返回结果类型就是什么。对于
const a = 2,a的类型就是2(let声明就是number)
- 使用条件类型优于重载 但是extends多种类型的话,就比较难办了
-
自定义类型以切断包类型依赖(只适用于依赖类型比较小的情况下)
-
类型测试
- 构造helper方法,判断参数类型和自定义类型是否“相等”
function assertType<T>(fn: T){}
assertType<number[]>(map(['john', 'mary'], name => name.length))
- 简单对象检查可分配性,
const 2可分配给number - 复杂对象的可分配性,子类型可以分配给父类型
// {name: string}[] = {name: string, name: number}[]
assertType<{name: string}[]>(() => ([{name: 'mary', age: 12}])) // ok
- 函数方法可分配性,带有更少输入参数可以分配给更多参数的函数类型 解决办法:利用内置方法ReturnType等,分开比较函数入参和函数出参
const g: (x: string) => any = () => 12 // ok
- 有一些我们习以为常的语法,不是javascript而是ts带来的
- class的private,protected, public标识。js private语法应该是
#privateField。如下图,ts编译前&编译后
总结:
本书的目的是指导读者怎么写出合适高效的ts(过于精确和模糊的ts都不好,啰嗦的ts,不可拓展的ts也不好)。这些技巧其实很多都已经融入到了我日常开发的使用中。
本书还穿插着一些讲解让你对ts的概念机制更加了解,因为大多数ts的教学更集中于用法上,能看到这些也是挺宝贵的。是一本挺好的ts入门书。
对于我来说,有点遗憾的是,高级复杂的ts用法比较少,ts编译,推断原理也没有提及。但是某些章节从作者的讲解思路也能窥探到一点点这部分。
虽然遗憾,但是作为一个前端开发来说,ts内部的机制确实不要求掌握。看了这本书之后,希望在以后使用ts遇到报错时,能有更加正确的解决思路。