mcpField
C
code-gen-independent

code-gen-independent

github.com/marius-townhouse开发648 次安装39 周前更新
代码仓库

关于 code-gen-independent

这项 TypeScript 技能说明类型检查与 JavaScript 生成彼此独立,且类型在运行时会被擦除。它帮助开发者改用实际的运行时检查与转换,而非依赖类型声明。

主要功能

  1. 01区分类型检查与代码生成
  2. 02说明运行时类型擦除
  3. 03展示属性与标签检查
  4. 04解释类型断言和实际转换
  5. 05识别基于类型的重载问题

适用场景

  1. 01排查有类型错误仍生成 JavaScript
  2. 02为接口选择运行时检查方式
  3. 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