TypeScript 为什么要增加一个 satisfies?

最近,在很多依赖库的类型定义文件中,经常能看到了一个陌生的朋友:satisfies

相信很多人都和我一样,看完 TypeScript 的相关文档,对这个关键字还是一头浆糊。

satisfies 关键字是 TypeScript 4.9 版本引入的,用于类型断言

先看一下连接数据库的例子:

typescript 复制代码
type Connection = {}

declare function createConnection(
  host: string,
  port: string,
  reconnect: boolean,
  poolSize: number,
): Connection;

这里,我们声明了一个函数 createConnection,它接收四个参数,返回一个 Connection 类型。

接着:

typescript 复制代码
type Config = {
  host: string;
  port: string | number;
  tryReconnect: boolean | (() => boolean);
  poolSize?: number;
}

我们又声明了一个 Config 类型,它包含了四个属性:hostporttryReconnectpoolSize

接下来:

typescript 复制代码
const config: Config = {
  host: "localhost",
  port: 3000,
  tryReconnect: () => true,
}

我们声明了一个 config 变量,它包含这三个属性的值:hostporttryReconnect

OK,现在我们来调用 createConnection 函数,并传入 config 参数:

typescript 复制代码
function main() {
  const { host, port, tryReconnect, poolSize } = config;
  const connection = createConnection(host, port, tryReconnect, poolSize);
}

问题出现了:

这里 port 的类型是 string | number,而 createConnection 函数的参数类型是 string,所以会报错。

为了解决类型定义问题,我们需要加上类型断言的逻辑代码:

typescript 复制代码
function main() {
  let { host, port, tryReconnect, poolSize } = config;

  if (typeof port === "number") {
    port = port.toString();
  }

  const connection = createConnection(host, port, tryReconnect, poolSize);
}

port 类型正确了,但 tryReconnect 类型错误了:

我们一次性将这些类型修复:

typescript 复制代码
function main() {
  let { host, port, tryReconnect, poolSize } = config;

  if (typeof port === "number") {
    port = port.toString();
  }
  if (typeof tryReconnect === "function") {
    tryReconnect = tryReconnect();
  }
  if (typeof poolSize === "undefined") {
    poolSize = 10;
  }

  const connection = createConnection(host, port, tryReconnect, poolSize);
}

porttryReconnectpoolSize 都进行了类型断言,问题解决了。

但是,这样写起来很麻烦,有没有更简单的方法呢?

一种方式是,去掉 config 的类型定义,放飞自我,让它自动被推断:

typescript 复制代码
const config = {
  host: "localhost",
  port: 3000,
  tryReconnect: () => true,
}

这样,我们可以一步到位:

typescript 复制代码
function main() {
  let { host, port, tryReconnect } = config;

  const connection = createConnection(host, port.toString(), tryReconnect(), 10);
}

但这样放飞类型,会引起另外的错误,比如 config 随便添加一个属性:

typescript 复制代码
const config = {
  host: "localhost",
  port: 3000,
  tryReconnect: () => true,
  pool: 10, // 新增了一个属性
}

这样 TypeScript 是一点都不会报错,但却会埋下隐藏炸弹,在代码上线的时候,可能会抓马,为什么 poorSize 不生效?

层层排查,最后才发现原来 poolSize 写错成了 pool

这个时候,satisfies,千呼万唤始出来:

typescript 复制代码
const config = {
  host: "localhost",
  port: 3000,
  tryReconnect: () => true,
  pool: 10,
} satisfies Config;

不负众望,TypeScript 终于报错,告诉我们 pool 属性不存在。

satisfies 关键字为我们提供了一种两全其美的解决方案:

  1. 保证类型安全 :它会检查我们的对象结构是否满足 (satisfies)指定的类型(如 Config)。如果你写了多余的属性(如 pool),或者属性类型不匹配,TypeScript 会立刻报错。这避免了"放飞自我"带来的隐患。
  2. 保留原始类型 :与使用类型注解 (: Config) 不同,satisfies 不会改变变量被推断出的原始具体类型 。变量 configport 属性类型仍然是 numbertryReconnect 属性类型仍然是 () => boolean

总结来说,satisfies 的核心优势在于:在不丢失(泛化)原始推断类型的前提下,对该值进行类型检查。

这使得我们既能获得编辑器对于具体类型的智能提示和类型推断的好处,又能确保这个值的结构符合我们预先定义好的更宽泛的类型约束,从而写出更安全、更灵活的代码。

REFERENCES

相关推荐
郭老二1 天前
【前端】vue3:编译步骤
前端
不在逃避q1 天前
Trae/Vs Code/Cursor命令行无法跑npm命令
前端·arcgis·npm
夏殇之殁1 天前
包中创建自定义列表项。 . 使用自定义列表项进行数据绑定 . 将天气预报数据保存到本地内存表,通过LiveBindings进行显示。 ...
服务器·前端·javascript
The Chosen One9851 天前
高进度算法模板速记(待完善)
java·前端·算法
陈随易1 天前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师1 天前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户059540174461 天前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈1 天前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen1 天前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒1 天前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端