除枚举(enum)外,TypeScript还有哪些具有运行时影响的语法构造?
我太懂你这种疑惑了!之前我一直想当然地觉得TypeScript的所有“类型相关”语法都会在编译阶段被彻底擦除,最终只留下纯JavaScript代码跑在生产环境里——直到发现枚举(enum)编译后居然会生成对应的JS对象,把键值对原原本本地保留下来,当时还挺颠覆认知的。
除了枚举之外,确实还有不少TypeScript语法构造在编译后会留下实实在在的运行时代码,给你梳理几个常见的:
命名空间(namespace/module)
当你用namespace封装代码时,编译后会生成立即执行函数表达式(IIFE)来创建一个全局对象,把命名空间里的导出内容挂载上去,以此避免全局变量污染。比如你写:namespace Utils { export function formatDate(date: Date): string { return date.toLocaleDateString(); } }编译后会生成类似这样的JS代码,
Utils对象在运行时是真实存在的:var Utils; (function (Utils) { function formatDate(date) { return date.toLocaleDateString(); } Utils.formatDate = formatDate; })(Utils || (Utils = {}));实际定义的类(Class)
注意这里说的是自己编写的类,不是declare class这种仅用于类型声明的“空壳”。TypeScript的类编译后会转译为对应的ES构造函数和原型链代码,运行时就是一个可正常实例化的JavaScript类。比如:class User { name: string; constructor(name: string) { this.name = name; } greet() { return `Hello, ${this.name}!`; } }编译后会生成标准的ES构造函数,你可以在运行时直接
new User('Alice')创建实例,原型链上的方法也能正常调用。装饰器(Decorators)
只要你在tsconfig.json里启用了装饰器相关的编译选项(比如experimentalDecorators和emitDecoratorMetadata),TypeScript的装饰器就会在编译后保留完整的调用逻辑。比如给类加个日志装饰器:function logClass(target: Function) { console.log(`Class ${target.name} was defined`); } @logClass class Product {}编译后会生成
logClass(Product)这样的调用,装饰器函数会在运行时执行,甚至可以修改类的原型、添加元数据,对运行时行为产生直接影响。类的访问修饰符(private/protected/readonly)
当你用private或protected修饰类的属性/方法时,如果编译目标是ES2022及以上,TypeScript会转译为JavaScript原生的私有字段(用#前缀),这些字段在运行时是真正不可外部访问的;如果是更低版本的ES,会通过命名约定+tslib辅助函数来模拟私有性。另外,readonly修饰的属性,编译后会通过Object.defineProperty设置为不可写,运行时根本没法修改这个属性的值。动态导入(import())
TypeScript支持的动态导入语法import('./module'),编译后会转译为浏览器或Node.js原生的import()函数调用,运行时会异步加载目标模块——这可是有实实在在的运行时行为,比如你可以用它实现代码分割,按需加载模块来优化首屏性能。CommonJS风格的导入导出(export = / import = require())
如果你用export =导出模块,或者import x = require('./module')导入,编译后会生成对应的module.exports和require()调用,在Node.js环境运行时会实际处理模块的加载和导出,完全是有运行时实体的。
内容来源于stack exchange

