文章索引
satisfies、as const 与类型收窄的日常
· 2 分钟
satisfies 出现之前,我们在「类型标注」和「类型推断」之间只能二选一。标注了就丢失精度,不标注就没有校验。它的价值在于两个都要。
场景一:配置对象
先看没有它的时候会发生什么:
ts
type Route = { path: string, prerender?: boolean }
// 标注:校验通过,但 routes.home.path 的类型退化成 string
const routes: Record<string, Route> = {
home: { path: '/', prerender: true },
posts: { path: '/posts' },
}
// 推断:保住了字面量类型,但拼错字段没人管
const routes2 = {
home: { path: '/', prerennder: true }, // 拼错了,无人报错
}
satisfies 同时拿到两边的好处:
ts
const routes = {
home: { path: '/', prerender: true },
posts: { path: '/posts' },
} satisfies Record<string, Route>
routes.home.path // 类型是 '/',不是 string
routes.home.prerennder // 编译期报错
场景二:和 as const 叠加
as const 负责把值冻结成最窄的字面量类型,satisfies 负责校验形状,两者叠加是常见组合:
ts
const themes = ['dark', 'light'] as const satisfies readonly string[]
type Theme = (typeof themes)[number] // 'dark' | 'light'
联合类型直接从数据里长出来,加一个主题只需要改一处。
场景三:穷尽检查
satisfies never 是最便宜的穷尽检查手段:
ts
function label(theme: Theme): string {
switch (theme) {
case 'dark': return '暗色'
case 'light': return '亮色'
default: {
const unreachable = theme satisfies never
throw new Error(`未处理的主题: ${unreachable}`)
}
}
}
未来给 themes 加了新值却忘记补 case,这里会在编译期亮红灯,而不是在运行时抛异常。
一条经验法则
写常量数据时先用推断,发现需要校验形状时加 satisfies,需要字面量联合时加 as const。显式标注留给函数签名和公共 API,那才是它的主场。