学会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系列的第五篇,下面是往期文章

相关推荐
「、皓子~13 分钟前
后台管理系统的诞生 - 利用AI 1天完成整个后台管理系统的微服务后端+前端
前端·人工智能·微服务·小程序·go·ai编程·ai写作
就改了16 分钟前
Ajax——在OA系统提升性能的局部刷新
前端·javascript·ajax
凌冰_18 分钟前
Ajax 入门
前端·javascript·ajax
京东零售技术33 分钟前
京东小程序JS API仓颉改造实践
前端
奋飛42 分钟前
TypeScript系列:第六篇 - 编写高质量的TS类型
javascript·typescript·ts·declare·.d.ts
老A技术联盟42 分钟前
从小白入门,基于Cursor开发一个前端小程序之Cursor 编程实践与案例分析
前端·小程序
风铃喵游1 小时前
构建引擎: 打造小程序编译器
前端·小程序·架构
sunbyte1 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | ThemeClock(主题时钟)
前端·javascript·css·vue.js·前端框架·tailwindcss
小飞悟1 小时前
🎯 什么是模块化?CommonJS 和 ES6 Modules 到底有什么区别?小白也能看懂
前端·javascript·设计
浏览器API调用工程师_Taylor1 小时前
AOP魔法:一招实现登录弹窗的全局拦截与动态处理
前端·javascript·vue.js