关于 ecmascript-over-typescript-features
这项 TypeScript 技能建议运行时代码优先使用标准 ECMAScript 功能,而非 `enum`、`namespace` 等 TypeScript 专属结构。它面向希望编写可移植 JavaScript 模式、同时继续使用 TypeScript 类型系统的开发者。
主要功能
- 01优先采用 ECMAScript 运行时功能
- 02用常量对象替代枚举
- 03用 ES 模块替代命名空间
- 04显式声明类属性
- 05区分仅类型与运行时功能
适用场景
- 01选择枚举的替代写法
- 02使用标准模块组织代码
- 03编写兼顾 JavaScript 可移植性的库
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill ecmascript-over-typescript-features安装后在 Claude Code 中输入/ecmascript-over-typescript-features确认已加载。
详细介绍
优先使用 ECMAScript 特性,而非 TypeScript 特性
概述
TypeScript 包含 enum 和 namespace 等 JavaScript 中不存在的专属特性。尽可能优先使用标准 ECMAScript 特性——它们的可移植性更好,也更容易被 JavaScript 生态理解,而且不会让你受限于 TypeScript 专属模式。
这并不意味着要完全避免 TypeScript——请充分使用它的类型系统。但对于运行时特性,应优先选择标准 JavaScript。
何时使用此技能
- 在 TypeScript 和 ECMAScript 特性之间进行选择
- 编写可能在没有 TypeScript 的环境中使用的可移植代码
- 考虑使用
enum、namespace或参数属性 - 面向多个 JavaScript 环境
- 编写供广泛使用的库代码
铁律
优先使用标准 ECMAScript 特性,而非 TypeScript 专属的运行时特性。类型使用 TypeScript,运行时行为使用 JavaScript。
检测
留意 TypeScript 专属的运行时特性:
// RED FLAGS - TypeScript-specific features
enum Status { Active, Inactive } // TypeScript enum
namespace MyLib { } // TypeScript namespace
class Foo { constructor(public x: number) {} } // Parameter properties
枚举:改用常量对象
// TypeScript enum - generates runtime code
enum Status {
Active = 'ACTIVE',
Inactive = 'INACTIVE',
}
// Generates: var Status = { Active: 'ACTIVE', ... }
// BETTER: Const object with type
const Status = {
Active: 'ACTIVE',
Inactive: 'INACTIVE',
} as const;
type Status = typeof Status[keyof typeof Status];
// Status = 'ACTIVE' | 'INACTIVE'
// Usage
function setStatus(status: Status) { }
setStatus(Status.Active); // OK
setStatus('ACTIVE'); // OK
setStatus('INVALID'); // Error!
命名空间:改用 ES 模块
// TypeScript namespace
namespace Utils {
export function formatDate(d: Date): string {
return d.toISOString();
}
export const PI = 3.14159;
}
// BETTER: ES Module
// utils.ts
export function formatDate(d: Date): string {
return d.toISOString();
}
export const PI = 3.14159;
// usage.ts
import { formatDate, PI } from './utils';
参数属性:明确声明
// TypeScript parameter properties
class Person {
constructor(
public name: string,
private age: number,
protected id: string
) {}
}
// BETTER: Explicit declarations
class Person {
name: string;
private age: number;
protected id: string;
constructor(name: string, age: number, id: string) {
this.name = name;
this.age = age;
this.id = id;
}
}
何时可以使用 TypeScript 特性
// Type-only features are fine - no runtime impact
type User = { name: string }; // TypeScript type
interface Config { } // TypeScript interface
type ID = string; // Type alias
// These compile away completely!
抵御压力的准则
选择特性时:
- 先问:这是否仅用于类型? TypeScript 类型很好用。
- 检查运行时影响:它会生成特殊代码吗?
- 考虑可移植性:没有 TypeScript 时也能运行吗?
- 优先选择标准特性:ECMAScript 特性无处不在。
- 说明例外情况:如果必须使用 TS 特性,请解释原因。
危险信号
| 反模式 | 问题 | 解决方案 |
|---|---|---|
用 enum 表示字符串联合类型 | TypeScript 专属,会生成代码 | as const 对象 |
namespace | TypeScript 专属模块系统 | ES 模块 |
| 参数属性 | TypeScript 专属语法 | 显式声明属性 |
常见的自我辩解
“枚举用起来更方便”
事实:使用 as const 的常量对象能提供同等的类型安全性,同时采用标准 JavaScript。
“命名空间能整理代码”
事实:ES 模块才是标准。使用文件和导入来整理代码。
“参数属性能少写代码”
事实:它们不是标准语法。显式声明属性更清晰,也更具可移植性。
快速参考
| 不要使用 | 改用 | 原因 |
|---|---|---|
enum | as const 对象 | 标准 JavaScript |
namespace | ES 模块 | 标准特性,树摇效果更好 |
| 参数属性 | 显式声明属性 | 更清晰、符合标准 |
import = | ES 导入 | 标准模块系统 |
总结
使用 TypeScript 的类型系统,但运行时代码应优先采用标准 ECMAScript 特性。这样能让代码更具可移植性、更容易被理解,也更经得起未来变化。
参考
- Dan Vanderkam 著《Effective TypeScript》第 2 版
- 第 72 条:优先使用 ECMAScript 特性,而非 TypeScript 特性
评论
支持 Markdown