第一次看到 satisfies 时,我觉得它和 as、类型注解差不多,都是在对象后面加一点类型信息。真正开始整理路由配置和主题表之后,才发现它的位置很特别:TypeScript 会检查这个值符合目标类型,同时保留值本身更具体的推断。
普通类型注解有时太宽
假设我们有一个颜色表,键只能是 primary 和 danger,值可以是字符串或 RGB 数组。直接把变量标成 Record<Color, string | number[]> 没有问题,但取出 palette.primary 时,编辑器只知道它是联合类型,字符串方法就需要额外判断。
type Color = "primary" | "danger";
const palette = {
primary: "#245d75",
danger: [232, 93, 63],
} satisfies Record<Color, string | number[]>;
palette.primary.toUpperCase(); // 保留为 string
palette.danger.map(String); // 保留为 number[]
如果键拼错,或者漏掉必需的键,检查仍然会报错。我们同时拿到了约束和精确推断,这正是配置对象最需要的组合。
我会在这些地方使用
第一类是路由、菜单、权限这类静态映射。对象的形状需要被统一约束,但每个成员又有自己的字面量信息。第二类是传给第三方库的配置:用 satisfies SomeConfig 能提前发现字段拼错,又不会把整个对象都扩宽成库定义。
它也很适合配合 as const,不过顺序和目标要想清楚。as const 让值变成只读字面量,satisfies 负责检查约束。若后续代码需要修改数组,就不要为了“类型更精确”随手加只读。
satisfies。它不会帮你转换数据
satisfies 只存在于类型检查阶段,不会验证接口返回值,也不会在运行时补默认值。外部 JSON 即使写了 satisfies,不可信的数据仍然要用校验库或手写检查处理。
它也不是为了消灭类型注解。函数参数、公开返回值和类成员仍然需要清楚的类型边界。对我来说,它主要让“静态配置对象”更舒服:错误能被发现,编辑器也知道每个值究竟是什么。一个小语法,解决的正好是日常里反复出现的小问题。