学会satisfies操作符,让你的Typescript功力倍增!

今日鸡汤: 人生是海, 总有波涛,需要一颗勇敢的心去乘风破浪!

大家好,我是心灵。

本文是在Typescript中旅行的第 5 篇。将介绍satisfies操作符。

satisfies运算符是一个在typescript项目中非常有用的运算符,在Typescript4.9版本中被引入。但是在日常开发中,强大的satisfies运算符却通常看不到有人在使用, 这可能是Typescript官方手册中并没有介绍这一特性,而仅在4.9版本记录中介绍了satisfies。不为人所熟知。

本文将会讨论如下几个问题:

  1. satisfies是什么且如何使用?
  2. 有什么作用?
  3. as的区别是什么?
  4. 使用场景

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.greenstring还是RGB,因为我们将palettevalue定义为了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

多余的属性检查

作用

  1. 保证类型的正确性,可以检查给定类型是否满足特定条件。

  2. 使用satisfies操作符在对变量进行管理的时候会准确的推断出值的类型,即使值是联合类型

  3. satisfies推断出来的是窄类型,而不是更宽的类型,且值优于类型。

与as 的不同

  • as表示断言,有一种强制的意思,而且最关键的一点是as可能会向编辑器说谎!,这是as的特性决定的,而satisfies不会。

红框中的代码会在运行时报错!

  • as const处理变量

as const也可以让palette.green也可以修改该报错, 但是as const 会将变量变为radonly类型。此时palette.green被推断为字面量类型, 我们无法对palette.green进行赋值操作。在有些时候我们不想要这样的效果。

  • 变量标注、as const与satisfies

    当我们想要标注一个变量的类型,但是又想要其不可变,可以用as const

    ts 复制代码
    interface Result {
    	retCode: 200 | 404 | 500
    }
    
    const responseTableData: Result = {
    	retCode: 200
    } as const
    
    responseTableData.retCode = 404 // ???

    当变量标注与as const一起配合使用时,比变量标注覆盖了as const ,所以无法达到我们想要的效果。

    这个时候satisfies能完成我们想做的事情。

    ts 复制代码
    interface Result {
            retCode: 200 | 404 | 500
    }
    
    const responseTableData = {
            retCode: 200
    } as const satisfies Result
    
    responseTableData.retCode = 404

使用场景和其他例子

  1. 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[]的形状。

  1. 配合never进行详尽性检查
  1. 根据业务需要我们定义了一个Student类型,Student的属性和值的类型被确定下来

    ts 复制代码
    type 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系列的第五篇,下面是往期文章

相关推荐
Shun_Tianyou5 分钟前
Python Day19 时间模块 和 json模块 及例题分析
java·服务器·前端
Gazer_S11 分钟前
【React Context API 优化与性能实践指南】
前端·javascript·react.js
w4ngzhen16 分钟前
COME,轻量级自托管的站点评论系统套件
前端·javascript
掘金酱17 分钟前
🎆仲夏掘金赛:码上争锋,金石成川 | 8月金石计划
前端·人工智能·后端
wordbaby17 分钟前
CSS 环境变量 env() 与自定义变量 var() 全面解读及实用场景
前端·css
oil欧哟40 分钟前
🧐 我开发的 AI 文本纠错/润色工具 Text-Well 上线了~
前端·ai编程·next.js
Mintopia40 分钟前
网格布尔运算的三重奏:从像素的邂逅到模型的重生
前端·javascript·计算机图形学
Apifox41 分钟前
Apifox 7 月更新|通过 AI 命名参数及检测接口规范、在线文档支持自定义 CSS 和 JavaScript、鉴权能力升级
前端·后端·测试
Mintopia43 分钟前
用 Three.js 构建组件库:一场 3D 世界的 "乐高" 之旅
前端·javascript·three.js
十五_在努力44 分钟前
参透 JavaScript —— 彻底理解原型与原型链
前端·javascript