类型收窄(条件逻辑的类型)
不要被名词吓到...就简单的经验概念
指 ts静态分析 自动推导未来运行时条件语句中的类型
本属于原理理论,但因为影响一些常用逻辑编写类型,因此要熟悉
if 语句 typeof instanceof
ts 内部需要识别 js 条件逻辑中的 typeof/instanceof
, ts 称这种识别为 类型保护 (type guard)
function printAll(strs: string | string[] | null) {
if (typeof strs === "object") {
for (const s of strs) {
// strs is possibly 'null'.
console.log(s);
}
} else if (typeof strs === "string") {
console.log(strs);
} else {
}
}
ts 保留 js typeof
语法的遗留问题(typeof null === 'object'
)
if 语句 隐式类型转换
js 的 条件语句(if
、&&
、||
、switch
) 存在隐式类型转换
如 0 、NaN、""、0n、null undefined
这些值都会被转为 false
,其他的值则会被转为 true
👆 同一个例子,js 的隐式类型转换,ts也可以识别,成功收窄类型
控制流分析(Control flow analysis)
若 padding
是 number
类型,无法达到 (unreachable
) 的部分,就会将 number类型从 number | string
类型中删除掉
这种基于可达性(reachability
) 的代码分析就叫做控制流分析(control flow analysis
)。
上面的都是ts内部自动推导的 类型收窄
下面是需要手动实现的 类型收窄
is type 手动类型收窄
function isString(s: any) {
return typeof s === 'string';
}
function toUpperCase(x: unknown) {
if(isString(x)) {
x.toUpperCase(); // ⚡️ x is still of type unknown
}
}
if
条件语句里 ts
不能自动执行函数 (有了 ts
后一般也很少要运行时判断类型了吧)
👇 使用 is + type
定义函数的返回
function isString(s: any): s is string { // <-- ✨ 入参 is type
return typeof s === 'string';
}
✨ 过滤数据时有用!!!
filter
等数组工具函数 callback
是返回 true/false
的函数, 在函数式编程里常抽离 原子函数
把这个原子函数定义成is,可以用于更多地方
interface Fish {}
interface Bird {}
function isFish(item: any): item is Fish {} // <-- ✨ 原子函数
const zoo: (Fish | Bird)[] = [fish1, fish2, bird1]
const afterList: Fish[] = zoo.filter(isFish)
可辨别联合(Discriminated unions)
👇 我们常遇到这样的数据结构, 不同的 itemType
走不同的逻辑
而 aParams
和 bParams
都是非必填的
ts 并不知道这种数据内部逻辑
👇 可以用非空断言解决(用 as
同理)
👇 但是我们希望类型声明可以更语义化,从 类型 上看出数据的内部逻辑
interface A {
itemType: 'a',
aParams: number
}
interface B {
itemType: 'b',
bParams: number
}
type AorB = A | B // <-- ✨ 可辨别联合(Discriminated unions)
function run(params: AorB) {
if(params.itemType === 'a') {
return params.aParams * 2 // ✅
}else{
return params.bParams * 2 // ✅
}
}
可辨别联合的应用远不止这些,比如
- 消息模式
- 客户端服务端的交互
- 状态管理框架
试想在消息模式中,我们会监听和发送不同的事件,这些都是以名字进行区分,不同的事件还会携带不同的数据,这就应用到了可辨别联合
// 消息 发布订阅
const info = {
typeName: 'doA',
params: {}
}
// 控制中心
function handle(info) {
if(info.typeName === 'doA') {
doFnA(info.params) // <-- 💥 ts 声明不了逻辑相关数据的类型 只能定义成 any
}
// ...
}