前言
TypeScript 4.9 将新增 satisfies
操作符,类似于 as
,但他更像一个不那么 strict
的 as
。
正文
下面我们举例介绍 satisfies
的使用场景。
旧时代的类型匹配
在说明之前,我们先来看一个以前旧时代的类型匹配 case :
interface IConfig {
a: string | number
}
/**
* 🟡 case 1 - 以前的写法,第一行声明会报错,但使用 legacy.a 时不会报错,这是有问题的
*/
const legacy: IConfig = {}
// ^^^^^^ 类型 "{}" 中缺少属性 "a",但类型 "IConfig" 中需要该属性。
legacy.a
// ^ (property) IConfig.a: string | number
/**
* 🔴 case 2 - 以前的另一种写法,声明和使用都不会报错,大错特错!
*/
const legacyAs = {} as IConfig
// Not error
legacyAs.a
// ^ (property) IConfig.a: string | number
小结
case 1
和 case 2
💩 一样的写法以前我们都可能用到,很多时候,我们明知道 case 2
是大错特错的,但是我们不想把这个 对象 给提取出来了,所以就直接写了 as
,这是万万不可取的。
旧时代的改进与解法
既然旧时代的两种写法都有问题,我们如何解 ?
interface IConfig {
a: string | number
}
/**
* 🟡 case 3 - 我们知道 case 2 大错特错,所以我们就基于 case 1 来编写,但是 `a` 的类型推断不出来
* 那么,我们为了有类型提示,只能多写一步 as 了。
*/
const legacyWithValue: IConfig = { a: 2 }
// Not error
legacyWithValue.a.toFixed()
// ^^^^^^^ 类型“string | number”上不存在属性“toFixed”
;(legacyWithValue.a as number).toFixed()
// Not error ^^ 🤮
小结
case 3
的写法在以前应该是最好的写法了,就是 先声明,再 as
,和个小丑 🤡 一样,那有没有更好的写法?
新时代的写法 :satisfies
在 TS 4.9 后,为了解决以上问题,我们应该如下编码:
interface IConfig {
a: string | number
}
/**
* 🟢 case 4 - 新写法,可以看到第一行声明会报错,使用时也会报错,两全其美,很 nice
*/
const current = {} satisfies IConfig
// ^^^^^^^ 类型“{}”不满足预期类型“IConfig”。
current.a
// ^ 类型“{}”上不存在属性“a”
/**
* 🟢 case 5 - 我们基于 case 4 把值加上,发现此时使用 a 的时候会自动推断我们声明的类型了!不再是联合类型。
*/
const currentWithValue = { a: 2 } satisfies IConfig
// Not error
currentWithValue.a.toFixed()
// ^ (property) a: number 🥰
小结
TS 4.9 以后,想写 as
的时候多想想,是不是应该用 satisfies
😎 了 ?
总结
satisfies
可以非常丝滑的解决 对象 的格式匹配问题,关于更多信息,请参见 Announcing TypeScript 4.9 Beta 。