关于 code-gen-independent
这项 TypeScript 技能说明类型检查与 JavaScript 生成彼此独立,且类型在运行时会被擦除。它帮助开发者改用实际的运行时检查与转换,而非依赖类型声明。
主要功能
- 01区分类型检查与代码生成
- 02说明运行时类型擦除
- 03展示属性与标签检查
- 04解释类型断言和实际转换
- 05识别基于类型的重载问题
适用场景
- 01排查有类型错误仍生成 JavaScript
- 02为接口选择运行时检查方式
- 03用实际转换替代类型断言
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill code-gen-independent安装后在 Claude Code 中输入/code-gen-independent确认已加载。
详细介绍
理解代码生成独立于类型
概述
TypeScript 编译和类型检查是两个独立的过程。
TypeScript 编译器会做两件事:(1)将 TypeScript 转译为 JavaScript;(2)检查类型。这两者彼此独立——类型错误不会阻止代码生成,类型在运行时也不存在。
何时使用此技能
- 不明白为什么有类型错误时,代码仍能运行
- 尝试在运行时检查类型
- 对接口使用
instanceof - 以为类型会影响运行时行为
铁律
NEVER expect TypeScript types to exist at runtime. They are erased.
关键事实:
- 有类型错误的代码仍可生成 JavaScript 输出
- 类型会在编译过程中被擦除
- 不能对 TypeScript 接口使用
instanceof - 运行时检查需要使用 JavaScript 结构
类型错误不会阻止输出
$ cat test.ts
let x = 'hello';
x = 1234; // Type error!
$ tsc test.ts
test.ts:2:1 - error TS2322: Type 'number' is not assignable to type 'string'
$ cat test.js
var x = 'hello';
x = 1234; // JavaScript was still generated!
类型错误就像警告,不会阻止编译。
不能在运行时检查类型
interface Square {
width: number;
}
interface Rectangle extends Square {
height: number;
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
if (shape instanceof Rectangle) {
// ~~~~~~~~~ 'Rectangle' only refers to a type,
// but is being used as a value here
return shape.width * shape.height;
}
return shape.width * shape.width;
}
为什么? interface 和 type 会被擦除,在 JavaScript 中并不存在。
运行时类型检查的解决方案
选项 1:检查属性
function calculateArea(shape: Shape) {
if ('height' in shape) {
// TypeScript knows shape is Rectangle here
return shape.width * shape.height;
}
return shape.width * shape.width;
}
选项 2:带标签的联合类型(推荐)
interface Square {
kind: 'square';
width: number;
}
interface Rectangle {
kind: 'rectangle';
width: number;
height: number;
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
if (shape.kind === 'rectangle') {
return shape.width * shape.height;
}
return shape.width * shape.width;
}
选项 3:使用类
class Square {
constructor(public width: number) {}
}
class Rectangle extends Square {
constructor(width: number, public height: number) {
super(width);
}
}
function calculateArea(shape: Square | Rectangle) {
if (shape instanceof Rectangle) {
// This works! Classes exist at runtime
return shape.width * shape.height;
}
return shape.width * shape.width;
}
类型操作不会影响运行时
function asNumber(val: number | string): number {
return val as number; // Type assertion
}
// Generated JavaScript:
function asNumber(val) {
return val; // No conversion! Just returns val as-is
}
类型断言不会转换值。请使用运行时代码:
function asNumber(val: number | string): number {
return typeof val === 'string' ? Number(val) : val;
}
运行时类型可能与声明的类型不同
function setLightSwitch(value: boolean) {
switch (value) {
case true: turnLightOn(); break;
case false: turnLightOff(); break;
default:
console.log("I'm afraid I can't do that.");
// TypeScript thinks this is unreachable, but...
}
}
// At runtime, someone could call:
setLightSwitch("ON" as any); // Hits the default case!
API 响应、外部数据和 any 类型都可能导致运行时类型不匹配。
不能基于类型进行重载
// ❌ This doesn't work - types are erased
function add(a: number, b: number) { return a + b; }
function add(a: string, b: string) { return a + b; }
// ~~~ Duplicate function implementation
// ✅ Use a single implementation with union type
function add(a: number | string, b: number | string) {
if (typeof a === 'number' && typeof b === 'number') {
return a + b;
}
return String(a) + String(b);
}
抵抗压力的规范
1. “直接用 as Type”
施压说法:“类型断言会转换值”
**回应:**断言不会转换值,它只在编译时生效。
**行动:**编写实际的运行时转换代码。
2. “使用 instanceof”
施压说法:“instanceof 应该能用于我的接口”
**回应:**接口在运行时并不存在。请使用带标签的联合类型或类。
**行动:**添加一个判别属性,或使用类。
危险信号——停止并重新考虑
- 对接口或类型使用
instanceof - 以为类型断言会转换值
- 认为类型错误会阻止 JavaScript 输出
- 依赖 TypeScript 类型进行运行时验证
快速参考
| TypeScript 结构 | 运行时是否存在? |
|---|---|
interface | 否 |
type | 否 |
类型注解(: T) | 否 |
类型断言(as T) | 否 |
class | 是 |
enum(非 const) | 是 |
| 带标签的联合类型属性 | 是 |
总结
类型会被擦除。代码生成独立于类型检查。
TypeScript 类型只在编译时存在。要进行运行时类型检查,请使用 JavaScript 结构:属性检查、带标签的联合类型、类,或 typeof/instanceof(用于值,而不是类型)。
参考资料
基于 Dan Vanderkam 的《Effective TypeScript》第 3 条:理解代码生成独立于类型。
评论
支持 Markdown