04|类型与静态检查
第九章 / 04。目标:让每种检查只承担它真的能证明的合同。
Vite 能构建不等于类型正确
Vite 开发与构建默认以转译为主,不做完整 TypeScript 类型检查;.vue 模板中的类型需要 vue-tsc。在已有 ESLint 配置的项目中,可以把反馈拆成下面的脚本。这是配置示意;第一章 --bare 实验没有 ESLint,不要直接添加或执行 lint,首次先运行该项目已有的 npm run type-check、npm run build 和本章 npx vitest run:
{
"scripts": {
"dev": "vite",
"typecheck": "vue-tsc --build --force",
"lint": "eslint .",
"test": "vitest run",
"build": "vite build"
}
}真实命令应按项目版本调整,但 CI 要分别保存退出状态,不能因最后 build 成功覆盖前面的 typecheck 失败。
在第一章 TypeScript 项目中,create-vue 已提供 vue-tsc 和多个 tsconfig;下面展示的检查项应按实际已有工具合并进 package.json,不用上面的短 JSON 覆盖 dependencies。给模板故意传错类型再运行 typecheck,确认错误来自模板而不只是脚本;修复后再跑 build。只有转译成功仍不能作为类型通过证据。
组件合同用类型表达
<script setup lang="ts">
type User = { id: string; displayName: string }
const props = defineProps<{
user: User
mode?: 'compact' | 'full'
}>()
const emit = defineEmits<{
select: [id: string]
remove: [id: string, reason?: string]
}>()
emit('select', props.user.id)
// emit('select', 42) // vue-tsc 应报错
</script>类型能阻止调用方传数字,却不能证明 API 返回的 JSON 真符合 User。JSON.parse(...) as User 只是断言,不是校验;网络、localStorage 和 URL 输入必须在运行时解析失败路径。
环境与规则边界
- 浏览器源码、Node 配置、测试文件应使用匹配的 lib/types;不要让全局
process或window在错误环境“碰巧有类型”。 - alias 同时配置在构建器与 TypeScript 中,否则 IDE 与运行结果会分裂。
- ESLint 处理未使用值、危险 Promise、依赖边界等代码规则;formatter 只负责布局。
- 提交钩子用于快速反馈,完整 typecheck/test 仍在 CI 执行,不能只检查暂存的几个文件就宣布全图正确。
练习:类型正确但运行失败
实现 parseUser(input: unknown): User,要求 id/displayName 为非空字符串,不允许额外的 sessionToken 字段;非法输入抛 TypeError。再写 UserCard.vue 的 typed props/emits,加入一行 @ts-expect-error 证明错误事件参数被检查。运行时测试必须验证成功、缺字段、错类型和敏感字段四类输入。
参考答案:第 9 章第 04 题。