TypeScript satisfies 运算符:校验与推导兼得
satisfies(TS 4.9+)让值在保持精确推导类型的同时满足类型约束。本文讲解它解决的类型拓宽问题与配置对象类型安全的实战模式。
config satisfies Config 检查 config 符合 Config 类型,但保留 config 自身的精确类型(字面量类型不拓宽)。与类型标注(会拓宽)和 as(不检查)相比,satisfies 是"既要校验又要精确"的唯一答案。
类型拓宽的问题
type Theme = 'light' | 'dark';
// 类型标注:安全但丢失精确性
const theme: Theme = 'light';
// theme 的类型是 Theme——不再是字面量 'light'
// as 断言:精确但不安全
const theme2 = 'light' as Theme;
// 写错也不报错:'ligt' as Theme 照样通过
标注丢失精确类型,断言放弃检查——两害之间的空白由 satisfies 填补。
satisfies 解法
const theme = 'light' satisfies Theme;
// ✅ 校验:必须是 Theme 的成员('ligt' 会报错)
// ✅ 精确:类型仍是字面量 'light'
实战:类型安全的配置对象
type Route = { path: string; method: 'GET' | 'POST' | 'PUT' | 'DELETE' };
const routes = {
listUsers: { path: '/users', method: 'GET' },
createUser: { path: '/users', method: 'POST' },
} satisfies Record<string, Route>;
// 精确推导的回报:
routes.listUsers.method // 类型是 'GET'(不是 'GET'|'POST'|...)
routes.createUser.path // 自动补全精确可用
routes.badRoute // 编译报错——key 不存在
API 路由表、主题配置、功能开关——所有"配置即代码"场景的标配写法。
与映射类型联动
satisfies 保留的字面量类型让后续类型编程更精确:
type RouteNames = keyof typeof routes; // 'listUsers' | 'createUser'
type GetRoutes = { [K in keyof typeof routes]: typeof routes[K]['method'] extends 'GET' ? K : never }[keyof typeof routes];
常见问题(FAQ)
Q:satisfies 和 as const 的区别?
as const 冻结值(深只读 + 全字面量化)但不做约束校验;satisfies 校验约束但不改变可变性。两者可组合:config satisfies Config 内部字段按需 as const。
Q:能用于函数返回值吗?
可以——return value satisfies Result 在返回处双重确认。配合显式返回类型注解,错误定位更精准。
Q:旧版本 TS 没有这个怎么写?
升级 TS。临时方案:辅助函数 const check = <T extends Route>(t: T) => t——但原生语法可读性完胜。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。