TypeScript 真正省时间的地方,往往不是把类型写得更花,而是在分支里让类型跟着数据变窄。收窄做好了,as 会明显变少。
判别联合
给对象加一个字面量字段,比一堆 optional 字段好维护:
type Result =
| { ok: true; data: string }
| { ok: false; error: string };
function render(result: Result) {
if (result.ok) {
return result.data; // data 可用
}
return result.error; // error 可用
}自定义类型守卫
当第三方数据或 unknown 进门时:
function isPost(value: unknown): value is { title: string; slug: string } {
if (!value || typeof value !== "object") return false;
const v = value as Record<string, unknown>;
return typeof v.title === "string" && typeof v.slug === "string";
}守卫返回 true 后,后续分支里类型会被收窄——这比盲目 as Post 更诚实。
satisfies:约束形状又保留字面量
satisfies 适合配置对象:既要符合接口,又不想被放宽成宽类型。
const categories = {
frontend: "前端",
backend: "后端",
ai: "AI 学习",
} as const satisfies Record<string, string>;
// categories.frontend 仍是 "前端" 字面量实践建议
- API 边界用
unknown+ 守卫/校验库,不要信任any。 - 组件 props 优先用判别联合表达互斥 UI 状态。
- 忍不住写
as时,先问能不能用收窄或提前校验替代。
小结
类型收窄是把运行时已经做的判断,同步告诉编译器。养成这个习惯后,重构会安全很多。