今日鸡汤: 人生是海, 总有波涛,需要一颗勇敢的心去乘风破浪!
大家好,我是心灵。
本文是在Typescript中旅行的第 5 篇。将介绍satisfies操作符。
satisfies运算符是一个在typescript项目中非常有用的运算符,在Typescript4.9版本中被引入。但是在日常开发中,强大的satisfies运算符却通常看不到有人在使用, 这可能是Typescript官方手册中并没有介绍这一特性,而仅在4.9版本记录中介绍了satisfies。不为人所熟知。
本文将会讨论如下几个问题:
- satisfies是什么且如何使用?
- 有什么作用?
- 和
as
的区别是什么? - 使用场景
satisfies是什么?
先不说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]
}
// Both of these methods are still accessible!
const greenNormalized = palette.green.toUpperCase() // ~~~~~~ error!
ts在编辑器上抛出了一个错误❌。
这是因为TypeScript不确定palette.green
是string
还是RGB
,因为我们将palette
中value
定义为了string | RGB
的联合类型。所以palette.green
可以是它们中的任何一个。而RGB
类型没有toUpperCase
方法,一切都是为了类型安全。
为了让这行表达式正常执行,我们可以使用类型缩小的概念,使用if语句手动验证属性的类型:
ts
let greenNormalized
if (typeof palette.green === 'string') {
greenNormalized = palette.green.toUpperCase()
}
使用if
进行类型缩小似乎有些冗余,因为作为开发者我们知道palette
对象中green
属性就是string
类型的。所以有没有一种办法能让Typescript确保值与类型匹配,但又希望保留该类型的更具体的类型用来进行类型推导 。听起来有些拗口,其实就是当我们想让palette.green
这个表达式根据它的值*'#00ff00'*去自动匹配string
类型,而不是string | RGB
。
可能你会想到as const
,但是它并不是很好的解决方案(后面会说到),现在我们使用satisfies
操作符对palette
变量进行管理后得到了我们想要的结果。
非常完美!我们没有做其他复杂的操作,仅只是使用了satisfies
修饰符,green被推断出来了string
类型,palette.green.toUpperCase()
表达式也就不会报错了。而且类型安全也不会丢失。
当尝试给blue定义一个number
类型的变量也会进行正确的类型安全校验。
当尝试给palette
定义一个不在预期范围中的key
。
多余的属性检查
作用
-
保证类型的正确性,可以检查给定类型是否满足特定条件。
-
使用satisfies操作符在对变量进行管理的时候会准确的推断出值的类型,即使值是联合类型。
-
satisfies推断出来的是窄类型,而不是更宽的类型,且值优于类型。
与as 的不同
- as表示断言,有一种强制的意思,而且最关键的一点是
as
可能会向编辑器说谎!,这是as
的特性决定的,而satisfies
不会。
红框中的代码会在运行时报错!
as const
处理变量
as const
也可以让palette.green
也可以修改该报错, 但是as const
会将变量变为radonly
类型。此时palette.green
被推断为字面量类型, 我们无法对palette.green
进行赋值操作。在有些时候我们不想要这样的效果。
-
变量标注、
as const
与satisfies当我们想要标注一个变量的类型,但是又想要其不可变,可以用
as const
tsinterface Result { retCode: 200 | 404 | 500 } const responseTableData: Result = { retCode: 200 } as const responseTableData.retCode = 404 // ???
当变量标注与
as const
一起配合使用时,比变量标注覆盖了as const
,所以无法达到我们想要的效果。这个时候satisfies能完成我们想做的事情。
tsinterface Result { retCode: 200 | 404 | 500 } const responseTableData = { retCode: 200 } as const satisfies Result responseTableData.retCode = 404
使用场景和其他例子
- satisfies译为满足,所以在一些对象需要满足某种特定形状或是特定属性时来使用。
ts
interface RouteRow {
path: string
redirect?: string
name?: string
component: any
}
const routes = [
{
path: '/',
component: 'main'
},
{
path: '/login',
component: 'login'
}
] as const satisfies RouteRow[]
as const
强制routes
的不可变性,satisfies RouteRow[]
强调routes
要满足RouteRow[]
的形状。
- 配合never进行详尽性检查
-
根据业务需要我们定义了一个
Student
类型,Student
的属性和值的类型被确定下来tstype Keys = 'name' | 'age' | 'company' | 'email' const staff = { name: '小明', age: 18, company: 'alibaba', email: '123456' } satisfies Partial<Record<Keys, string | number>> staff.name.toLowerCase() staff.age.toFixed()
上面的
name
属性和age
属性都准确推断出了类型。
总结
satisfies运算符提供了一种灵活的方式来进行类型检查,同时保留了表达式的原始类型。如果使用了satisfies运算符ts会根据提供的类型和变量的值对变量进行类型检查和推断。推断的类型会更窄,能够准确的推断出值的类型。在下次需要使用类型缩小或是as
之前,可以先考虑到是否能够替换成satisfies
来解决。
往期文章
本篇是Typescript系列的第五篇,下面是往期文章