← 中文资源

简体中文版本

TypeScript Satisfies 配置推断

用 satisfies 运算符约束配置对象:形状错误编译期报错,同时保留键与值的字面量推断,对照类型注解会拓宽类型的陷阱。

阅读英文原文

示例说明

校验配置对象有两个互相冲突的诉求:既想强制它符合目标形状(写错字段名要报错),又想保留字面量类型(键是具体路径而非笼统 string)。类型注解满足前者牺牲后者,satisfies 两全其美——校验通过后 typeof 仍能推出精确的字面量联合。路由表、事件映射、开关配置这类场景收益最直接。

技术要点

  • satisfies 校验形状不拓宽字面量
  • keyof typeof 推出具体键联合
  • 对照注解赋值的拓宽陷阱

代码解读

routes 用 satisfies 声明三个路由:RoutePath 类型是三个路径的精确联合——把不存在的路径赋给 critical 直接编译报错;AllowedMethod 从具体路由的 methods 数组收窄出 GET 与 POST 的联合。对照段里 widened 的注解赋值让键类型退化为 string,两种写法的差异一目了然。

使用建议

readonly 数组配合 satisfies 能同时锁住元素与长度语义。as const 保留字面量但跳过校验,satisfies 是它的安全版。嵌套深的大型配置表尤其受益。

代码示例

type RouteConfig = Record<string, {
  methods: readonly ('GET' | 'POST')[];
  rateLimit?: number;
}>;

const routes = {
  '/healthz': { methods: ['GET'] },
  '/orders': { methods: ['GET', 'POST'], rateLimit: 120 },
  '/admin/purge': { methods: ['POST'], rateLimit: 5 },
} satisfies RouteConfig;

// satisfies 保留字面量推断:键类型是三个具体路径而非 string
type RoutePath = keyof typeof routes;
const critical: RoutePath = '/admin/purge';

// 联合收窄到真实出现的值
type AllowedMethod = (typeof routes)['/orders']['methods'][number];
const m: AllowedMethod = 'POST';

function routeLimit(path: RoutePath): number {
  return routes[path].rateLimit ?? 60;
}

console.log(critical, m, routeLimit('/healthz'), routeLimit('/orders'));

// 对照:类型注解会拓宽字面量,键类型退化为 string
const widened: RouteConfig = {
  '/healthz': { methods: ['GET'] },
};
type WidenedKey = keyof typeof widened; // string
const anyPath: WidenedKey = '/anything-goes';