如何开发同时支持JavaScript与TypeScript的TypeScript类库?
编写同时支持JavaScript与TypeScript的TypeScript类库指南
核心原则
遵循「API一致性」:确保TypeScript和JavaScript用户调用库的方式完全一致,避免出现仅TS可用或仅JS可用的API,参考Vue的设计思路——让两种环境下的API体验无差异。
具体实践建议
禁用TypeScript专属编译时特性
不要依赖参数装饰器、类装饰器这类仅在TS编译阶段生效的特性,它们编译后不会保留有效JS逻辑,会导致JS用户无法正常使用API。
反例(依赖装饰器的代码):class DataService { constructor(@Inject('API_CONFIG') private config: ApiConfig) {} }正例(JS友好的写法):
class DataService { constructor(config: ApiConfig) { this.config = config; } } // 额外提供工厂函数简化调用 export function createDataService(config: ApiConfig) { return new DataService(config); }基于标准JS语法设计API
优先使用函数、普通对象、原生类(不含TS专属修饰符)来设计API,这些结构编译后与原生JS表现完全一致。比如Vue的defineComponent本质是接收普通对象的函数,TS用户能获得类型提示,JS用户直接传对象即可调用。
示例:interface ComponentOptions { props?: Record<string, any>; setup?: () => void; } export function defineComponent(options: ComponentOptions) { // 内部处理逻辑 return options; }类型定义与JS逻辑分离
用interface、type编写完整的类型定义(开启tsconfig.json的declaration: true生成.d.ts文件),为TS用户提供IDE友好的类型提示,但不要让类型定义影响JS代码的运行。同时在JS层面做必要的参数校验,避免TS的编译时检查缺失导致JS用户出错。
示例:interface InitConfig { timeout: number; baseUrl: string; } export function init(config: InitConfig) { // JS层面参数校验 if (typeof config.timeout !== 'number' || config.timeout <= 0) { throw new Error('timeout必须是大于0的数字'); } // 核心逻辑 }配置TS编译确保JS产物兼容性
在tsconfig.json中合理设置:target:选择兼容范围广的ES版本(如ES2018),避免编译出过于前沿的语法;module:根据库的使用场景选择ESNext或CommonJS;declaration: true:生成类型定义文件;isolatedModules: true:确保每个文件可独立编译,避免编译后出现异常代码。
同时覆盖JS和TS测试场景
编写测试用例时,既要用TS验证类型提示和类型检查是否正常,也要用JS编写测试,确保API在纯JS环境下能正常调用、参数传递和返回值符合预期。
需要避免的坑
- 不要用
enum作为对外API的一部分:TS的enum编译后生成的对象在不同编译配置下可能存在差异,建议用as const标记的对象字面量替代:// 推荐写法 export const Status = { Active: 0, Inactive: 1 } as const; - 避免使用
namespace:改用ES模块的import/export,namespace编译后的JS代码结构不直观,会增加JS用户的导入成本。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

