使用TS Compiler API如何打印完全化简后的交叉类型?
问题原因
TypeScript 编译器的 typeToString 方法默认优先保留类型的原始书写形式,交叉类型、泛型包装类型都会直接输出原始引用,不会自动折叠为结构类型,只有单一非组合的映射类型才会默认展开。
优雅解决方案(无需注入自定义类型)
方案1:使用getApparentType + 格式化Flag(最简单)
typeChecker.getApparentType 会自动展开交叉类型、映射类型的结构,返回类型的表观结构类型,配合StructuralTypeDisplayFlag即可直接输出折叠后的结构:
const stmt = sourceFile.statements[2] as ts.TypeAliasDeclaration; const originalType = typeChecker.getTypeAtLocation(stmt.type); // 获取表观类型,自动展开交叉、映射类型结构 const apparentType = typeChecker.getApparentType(originalType); const str = typeChecker.typeToString( apparentType, stmt.type, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.InTypeAlias | ts.TypeFormatFlags.StructuralTypeDisplay ); console.log(str); // 输出预期结果:{ foo: string; bar?: string; }
方案2:自定义递归遍历类型(适合复杂嵌套场景)
如果需要处理深层嵌套的交叉/映射类型,或者需要自定义格式化规则,可以直接遍历类型属性生成字符串:
function expandTypeToString(type: ts.Type, checker: ts.TypeChecker, flags: ts.TypeFormatFlags): string { // 处理对象类型 if (type.getFlags() & ts.TypeFlags.Object) { const apparentType = checker.getApparentType(type); const props = apparentType.getProperties(); const propStrings = props.map(prop => { const propType = checker.getTypeOfSymbolAtLocation(prop, apparentType); const optional = (prop.getFlags() & ts.SymbolFlags.Optional) ? '?' : ''; return `${prop.name}${optional}: ${expandTypeToString(propType, checker, flags)}`; }); return `{ ${propStrings.join('; ')} }`; } // 非对象类型直接走默认格式化 return checker.typeToString(type, undefined, flags); } // 调用示例 const stmt = sourceFile.statements[2] as ts.TypeAliasDeclaration; const originalType = typeChecker.getTypeAtLocation(stmt.type); const str = expandTypeToString(originalType, typeChecker, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.InTypeAlias); console.log(str);
两种方案都不需要注入自定义工具类型到源文件,完全基于TypeScript Compiler API原生能力实现,性能和可维护性都优于注入类型的hack方案。
内容的提问来源于stack exchange,提问作者Zack Radisic
相关产品推荐
相关产品推荐

