示例说明
校验配置对象有两个互相冲突的诉求:既想强制它符合目标形状(写错字段名要报错),又想保留字面量类型(键是具体路径而非笼统 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';