作者给ts提了建议想要typescript把compiler暴露出来:
https://github.com/microsoft/TypeScript/issues/47658
开发文档:
https://github.com/deepkit/deepkit-framework/blob/master/DEVELOPMENT.md
Deepkit 运行时类型与字节码实现
Deepkit Enterprise TypeScript Framework 通过其独特的运行时类型系统,为类型检查、类型转换和约束验证提供了强大的支持。核心创新在于使用字节码(Bytecode)而非传统的可序列化 JavaScript 对象来表示类型信息。这个框架运行的本质是hack了typescript的源码,把一些类型信息在转换过程中保存了下来,并做了一层转换(最终是bytecode)。 本文将详细探讨 Deepkit 为什么选择字节码、其类型检查机制、支持的功能,以及具体的实现方式。
一、为什么 Deepkit 选择字节码表示类型
1. 背景与问题
传统的类型信息存储方式(如 reflect-metadata
或序列化的 JavaScript 对象)在复杂场景下存在以下问题:
- 体积庞大:序列化复杂类(包含属性可见性、只读、初始化器等)会导致生成的 JavaScript 对象过大,增加包体积。
- 运行时开销:解析和操作大型对象会显著增加执行时间和内存占用。
- 动态计算限制:序列化的静态对象无法支持运行时类型计算,例如解析泛型参数或动态类型推断。
2. 字节码的优势
Deepkit 选择字节码表示类型信息,原因如下:
- 代码体积小:字节码是一种紧凑的编码格式,相比冗长的 JSON 对象,发出的 JavaScript 代码更小。
- 动态类型计算:字节码支持运行时解释和计算,例如处理泛型类型参数或类型兼容性检查。
- 低开销:类型信息仅在需要时解析,减少不必要的运行时负载。
- 扩展性:字节码架构为未来实现更复杂的类型检查(如 TypeScript 文件完整编译)奠定了基础。
3. 对比序列化对象
- 序列化对象:静态生成,无法动态调整类型表示,适合简单场景但不灵活。
- 字节码:通过运行时解释器动态生成类型,适合复杂类型系统,支持按需计算。
二、Deepkit 的类型检查机制
1. 当前实现
Deepkit 的字节码解释器专注于静态类型的编译和检查:
- 限制:不支持类型推断或控制流分析,仅处理静态定义的类型。
- 功能:支持
extends
类型运算符,用于检查类型兼容性。 - 示例:可以判断类型 A 是否继承自类型 B,但不支持更复杂的类型差异分析。
2. 未来潜力
字节码设计为扩展提供了可能性:
- 完整 TypeScript 编译:将整个 TypeScript 文件编译为字节码,在快速虚拟机(VM)中执行。
- 高性能 VM:用 C++ 或 Rust 实现虚拟机,提升类型检查速度。
- 错误收集:在 VM 中捕获验证错误,构建高效类型检查器。
- 缓存优化:将字节码存储为二进制文件,按需加载,进一步加速类型检查。
这种架构可能显著提升 TypeScript 的类型检查性能并简化缓存管理。
三、Deepkit 支持的功能
Deepkit 的运行时类型系统支持以下功能:
- 类型校验:验证对象是否符合预期类型。
- 类型转换:将数据转换为目标类型。
- 约束条件:定义类型约束(如非空、范围)。
- 约束类型:支持高级约束逻辑。
- 自定义约束规则:允许用户扩展约束验证。
四、Deepkit 如何实现字节码表示
1. 类型解析过程
Deepkit 通过修改 TypeScript 编译器(tsc
)并注入自定义逻辑,将类型信息编码为字节码。流程如下:
- 编译时:
@deepkit/type-compiler
拦截 TypeScript 类型信息。 - 序列化:将类型信息打包为紧凑的
Packed
格式。 - 注入:将字节码挂载到目标对象的
__type
属性。 - 运行时:通过
reflect()
方法解析字节码,生成Type
对象。
2. __type
的挂载机制
- 编译时挂载:
- 使用
@deepkit/type-compiler
将类型信息编码为字节码。 - 将字节码附加到类或函数的
__type
属性。
- 使用
- 错误处理:
- 如果对象缺少
__type
属性且不是无参数函数,会抛出错误,提示安装编译器。
- 如果对象缺少
3. Processor 的处理流程
Processor
类负责解析字节码并执行类型计算(文件路径:deepkit-framework/packages/type/src/reflection/processor.ts
)。
- 核心结构:
class Processor { private cache: Program[] = []; protected program: Program = { active: false, frame: { index: 0, startIndex: -1, inputs: [], variables: 0 }, stack: [], // 执行栈 stackPointer: -1, // 栈指针 program: 0, // 当前程序计数器 depth: 0, // 嵌套深度 initialStack: [], // 初始栈 resultType: { kind: ReflectionKind.unknown }, // 结果类型 inputs: [], // 输入参数 end: 0, // 程序结束位置 ops: [], // 操作指令集 started: 0, // 开始时间 }; }
- 工作原理:
ops
存储字节码指令。stack
和frame
维护执行状态。- 通过解释字节码生成最终的类型表示。
4. 字节码格式
字节码是一种紧凑的操作序列(详见 Deepkit 文档),例如:
ReflectionOp.number
:表示数字类型。ReflectionOp.property
:定义属性。ReflectionOp.class
:声明类。
五、代码示例
示例 1:简单函数和类
-
源代码:
function say(text: string): void {} return class User { id: number; username: string }
-
编译结果:
function say(text) {} say.__type = ['text', 'say', 'P&2!$/\\"']; // 函数类型字节码 return _a = class User {} _a.__type = ['id', 'username', 'User', '\\\\'3!&3\\"5w#']; // 类类型字节码
示例 2:带注释的类和函数
-
源代码:
/** This is my class */ class User { id: number; username: string } /** My function */ function p() {}
-
编译结果:
// This is my class\\n class User {}\\n User.__type = ['id', 'username', 'User', '\\\\'3!&3"5w#']; // My function\\n function p() {}\\n p.__type = ['p', 'P\\"/!'];
-
字节码解码(
\\'3!&3"5w#
):[ ReflectionOp.number, // id: number ReflectionOp.property, 0, // 属性 'id' ReflectionOp.string, // username: string ReflectionOp.property, 1, // 属性 'username' ReflectionOp.class, // 类声明 ReflectionOp.typeName, 2, // 类名 'User' ];
六、总结
Deepkit 选择字节码表示类型信息是为了追求更小的代码体积、更低的运行时开销和更高的动态计算能力。其实现通过修改 TypeScript 编译器,在编译时将类型信息编码为字节码并注入到 __type
属性,运行时通过 Processor
解释字节码生成类型对象。这种设计不仅高效支持静态类型检查,还为未来的高性能类型检查器奠定了基础。
Deepkit 的字节码架构是其运行时类型系统的核心创新,适用于需要高性能类型管理的现代 TypeScript 应用。
Deepkit 的依赖注入与 TypeScript 集成
Deepkit Framework 的依赖注入(Dependency Injection, DI)实现独具特色,它摒弃了对 reflect-metadata
的依赖,转而使用自定义的运行时类型系统。这种设计不仅性能更优,还与框架的其他功能(如验证、序列化)无缝集成。本文将详细探讨其实现原理、与 TypeScript 的集成方式以及类型信息的存储与导出机制。
一、Deepkit 依赖注入的核心实现
1. 运行时类型系统:@deepkit/type
- 核心功能:通过
@deepkit/type
包提供运行时类型支持。 - 关键组件:
TypeClass
和TypeProperty
:定义类型结构。typeDecorators
:存储类型装饰器信息。annotateClass
:为类添加运行时类型信息。
- 示例:
export function annotateClass<T>(clazz: ClassType, type?: ReceiveType<T>) { (clazz as any).__type = resolveRuntimeType(type); }
2. 依赖注入核心逻辑:@deepkit/injector
- 核心文件:
injector.js
、provider.js
、module.js
。 - 实现方式:
InjectorContext
:依赖注入容器,管理依赖解析。Module
:模块化组织依赖,支持嵌套和隔离。
- 优势:通过模块化设计实现灵活的依赖管理。
3. 类型信息的获取
- 编译时:使用
@deepkit/type-compiler
生成类型信息。 - 运行时:通过
reflect()
函数访问完整类型信息。 - 特点:无需
reflect-metadata
的额外装饰器。
二、与 reflect-metadata
的对比
1. reflect-metadata
的性能开销
- 元数据存储:
const Metadata = new WeakMap(); Reflect.metadata = function (metadataKey, metadataValue) { return function (target, propertyKey) { Metadata.set(target, { [propertyKey]: { [metadataKey]: metadataValue } }); }; };
- 运行时反射:
const type = Reflect.getMetadata("design:type", target, propertyKey); const paramTypes = Reflect.getMetadata("design:paramtypes", target);
- 问题:每次访问需通过
WeakMap
查找,性能开销较大。
2. Deepkit 的优化实现
-
编译时生成:
class MyClass { static __type = [{ kind: ReflectionKind.class, ... }]; } const type = MyClass.__type; // 直接访问
-
装饰器开销:通过
type-compiler
在编译时处理,避免运行时反射。 -
优点:
- 性能更优:无需运行时反射。
- 类型支持更完整:包括泛型等高级特性。
三、Deepkit 如何集成 TypeScript
Deepkit 通过自定义转换器(Transformer)“hack”进 TypeScript 编译管道,具体实现如下:
1. 修改 TypeScript 编译器
- 目标:修改
tsc
内部的getTransformers
函数。 - 原始代码(
microsoft/TypeScript/src/compiler/program.ts
):export function getTransformers( compilerOptions, customTransformers, emitOnly ) { return { scriptTransformers: getScriptTransformers( compilerOptions, customTransformers, emitOnly ), declarationTransformers: getDeclarationTransformers(customTransformers), }; }
- 修改后:
function getTransformers(compilerOptions, customTransformers, emitOnly) { try { const typeTransformer = require("@deepkit/type-compiler"); if (typeTransformer) { customTransformers = customTransformers || {}; customTransformers.before = customTransformers.before || []; if (!customTransformers.before.includes(typeTransformer.transformer)) { customTransformers.before.push(typeTransformer.transformer); } customTransformers.afterDeclarations = customTransformers.afterDeclarations || []; if ( !customTransformers.afterDeclarations.includes( typeTransformer.declarationTransformer ) ) { customTransformers.afterDeclarations.push( typeTransformer.declarationTransformer ); } } } catch (e) {} return { scriptTransformers: getScriptTransformers( compilerOptions, customTransformers, emitOnly ), declarationTransformers: getDeclarationTransformers(customTransformers), }; }
- 钩子时机:
before
:在代码转换前执行。afterDeclarations
:在声明文件生成后执行。
2. 自定义转换器实现
-
核心类:
ReflectionTransformer
。 -
逻辑:
export class ReflectionTransformer implements CustomTransformer { transformSourceFile(sourceFile: SourceFile): SourceFile { const visitor = (node: Node) => { if (isClassDeclaration(node) && this.isWithReflection(sourceFile, node)) { const type = this.getTypeOfType(node); const __type = this.f.createPropertyDeclaration( [this.f.createModifier(ts.SyntaxKind.StaticKeyword)], '__type', undefined, undefined, type ); return this.f.updateClassDeclaration( node, node.modifiers, node.name, node.typeParameters, node.heritageClauses, [...node.members, __type] ); } return visitEachChild(node, visitor, this.context); }; return visitNode(sourceFile, visitor); } }
3. 构建工具集成
-
Vite 插件:
export function deepkitType(options: Options = {}) { const filter = createFilter(options.include ?? ['**/*.ts'], options.exclude ?? 'node_modules/**'); return { name: 'deepkit-type', enforce: 'pre', transform(code: string, fileName: string) { if (!filter(fileName)) return null; const transformed = ts.transpileModule(code, { compilerOptions: { target: ts.ScriptTarget.ESNext, module: ts.ModuleKind.ESNext }, transformers: { before: [transformer], afterDeclarations: [declarationTransformer], }, fileName, }); return { code: transformed.outputText, map: transformed.sourceMapText }; }, }; }
四、类型信息的存储与导出
1. 存储方式
-
类类型:直接存储在静态属性
__type
中。class MyClass { static __type = [{ kind: ReflectionKind.class, ... }]; }
-
其他类型(接口、类型别名等):存储在全局
TypeRegistry
中。export class TypeRegistry { private static types: Map<string, Type> = new Map(); static register(name: string, type: Type) { this.types.set(name, type); } static get(name: string) { return this.types.get(name); } }
-
JIT 容器:缓存运行时生成的类型信息。
const jitSymbol = Symbol("jit"); export function getTypeJitContainer(type: Type) { return type[jitSymbol] || (type[jitSymbol] = {}); }
-
全局作用域:存储在
globalThis
或window
中。if (!envGlobal["__type_registry"]) { envGlobal["__type_registry"] = new Map(); }
2. 导出机制
- 编译时:通过自定义 Transformer 将类型信息注入抽象语法树(AST)。
const newStatements = [ ...sourceFile.statements.slice(0, indexOfFirstLiteralExpression + 1), ...newTopStatements, // 插入类型相关的语句 ...sourceFile.statements.slice(indexOfFirstLiteralExpression + 1), ]; this.sourceFile = this.f.updateSourceFile(this.sourceFile, newStatements);
- 类和函数类型:直接添加静态属性
__type
。class Model { static __type = pack(ReflectionOp.string); // 编码后的类型信息 title: string; }
3. 使用流程
-
编译时:
const __type_User = { kind: ReflectionKind.objectLiteral, types: [...] }; TypeRegistry.register('User', __type_User);
-
运行时:
function getClassType(target: any) { return target.__type; } function getType(typeName: string) { return TypeRegistry.get(typeName); }
五、Deepkit 设计的优点
- 性能优化:
- 编译时生成类型信息,避免运行时反射。
- JIT 容器缓存动态生成的对象。
- 类型支持:
- 支持泛型、接口等高级 TypeScript 特性。
- 集成性:
- 与验证、序列化等功能无缝协作。
- 灵活性:
- 模块化依赖注入,支持跨模块类型共享。