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

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

大家好,我是心灵。

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

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

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

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

satisfies是什么?

先不说satisfies晦涩的官方定义,我们先来看官网的一个朴实无华的例子(略作修改,更好理解):

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在编辑器上抛出了一个错误❌。

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

这是因为TypeScript不确定palette.greenstring还是RGB,因为我们将palettevalue定义为了string | RGB的联合类型。所以palette.green可以是它们中的任何一个。而RGB类型没有toUpperCase方法,一切都是为了类型安全。

为了让这行表达式正常执行,我们可以使用类型缩小的概念,使用if语句手动验证属性的类型:

let greenNormalized
if (typeof palette.green === 'string') {
	greenNormalized = palette.green.toUpperCase() 
}

使用if进行类型缩小似乎有些冗余,因为作为开发者我们知道palette对象中green属性就是string类型的。所以有没有一种办法能让Typescript确保值与类型匹配,但又希望保留该类型的更具体的类型用来进行类型推导。听起来有些拗口,其实就是当我们想让palette.green这个表达式根据它的值*’#00ff00’*去自动匹配string类型,而不是string | RGB

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

可能你会想到as const,但是它并不是很好的解决方案(后面会说到),现在我们使用satisfies操作符对palette变量进行管理后得到了我们想要的结果。

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

非常完美!我们没有做其他复杂的操作,仅只是使用了satisfies修饰符,green被推断出来了string类型,palette.green.toUpperCase()表达式也就不会报错了。而且类型安全也不会丢失。

当尝试给blue定义一个number类型的变量也会进行正确的类型安全校验。

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

当尝试给palette定义一个不在预期范围中的key

学会satisfies操作符,让你的Typescript功力倍增!
多余的属性检查

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

作用

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

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

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

与as 的不同

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

学会satisfies操作符,让你的Typescript功力倍增!
红框中的代码会在运行时报错!

  • as const处理变量

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

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

  • 变量标注、as const与satisfies

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

    interface Result {
    	retCode: 200 | 404 | 500
    }
    
    const responseTableData: Result = {
    	retCode: 200
    } as const
    
    responseTableData.retCode = 404 // ???
    

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

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

    interface Result {
            retCode: 200 | 404 | 500
    }
    
    const responseTableData = {
            retCode: 200
    } as const satisfies Result
    
    responseTableData.retCode = 404
    

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

使用场景和其他例子

  1. satisfies译为满足,所以在一些对象需要满足某种特定形状或是特定属性时来使用。
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进行详尽性检查

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

  1. 根据业务需要我们定义了一个Student类型,Student的属性和值的类型被确定下来

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

原文链接:https://juejin.cn/post/7350209063520927782 作者:xinling_any

(0)
上一篇 2024年3月27日 上午10:28
下一篇 2024年3月27日 上午10:39

相关推荐

发表回复

登录后才能评论