satisfies 操作符
写 ts 时,有时候我们希望表达式能与某个类型匹配,同时又能有准确的推断能力:
type Colors = 'red' | 'green' | 'blue'
type RGB = [red: number, green: number, blue: number]
const palette: Record<Colors, string | RGB> = {
red: [255, 0, 0],
green: '#00ff00',
blue: [0, 0, 255],
}
如上 palette 的键值均有约束,但是对于每个属性值无法做出准确地推断:
// 期望是string类型,但会得到 string | RGB
palette.green
// 同理期望 期望RGB
palette.red
而如果我们去掉 Record<Colors, string | RGB> 后,上诉推断是可以生效了,但声明的时候就无法保证匹配想要的类型的:
const a = {
// 不小心打错了,也不会报错
belu: '#ffffff',
}
而 satisfies 可以完美地解决如上两个问题:
const palette = {
red: [255, 0, 0],
green: '#00ff00',
bleu: [0, 0, 255],
// ~~~~ bleu 是个非法的key
} satisfies Record<Colors, string | RGB>
// 是个数组
const redComponent = palette.red.at(0)
// 是个字符串
const greenNormalized = palette.green.toUpperCase()
in 操作符收窄类型
in 操作符可以用来判断某个对象是否有某个 key,而也可以用来收窄类型:
interface RGB {
red: number
green: number
blue: number
}
interface HSV {
hue: number
saturation: number
value: number
}
function setColor(color: RGB | HSV) {
if ('hue' in color) {
// 此时可以收窄到 HSV类型
}
// ...
}
再过去,但是对于哪些没有在类型中列出来的属性,则无法做收窄,会报错
function foo(package: unknown) {
if (typeof package === 'object') {
if ('name' in package && typeof package.name === 'string') {
// error
package.name
}
}
}
package 被收窄到 object,而 in 会严格收窄到实际有列出要检查属性的类型,因此还是 object
而在 4.9 版本,在做 in 判断时,如果检查的属性是什么再类型里列出来的,则会将类型收窄成 object & Record<'name', unknown>,然后判断 typeof package.name === 'string' 后,进一步收窄到 object & Record<'name', string>
同时对 in 还做了进一步检查,in 的左侧可分配的类型是 string | number | symbol,右侧分配给 object,避免意外检查原始类型