能否为单个类生成多个接口/类?SignalR Hub类场景咨询
实现单个C# Hub类生成多个TypeScript接口的方案
要实现从带[TsInterface]特性的SignalR Hub类生成你期望的三个TypeScript接口,最靠谱的方式是用**.NET源代码生成器(Source Generator)**——它能在编译时自动扫描带特性的类,生成对应的TS代码,完全匹配你的需求。
具体步骤和实现思路
1. 先定义[TsInterface]特性(如果还没定义)
首先得有这个标记特性,用来标识需要生成TS接口的Hub类:
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public sealed class TsInterfaceAttribute : Attribute { // 可按需添加配置参数,比如指定TS文件输出路径等 }
2. 编写源代码生成器
创建一个.NET类库项目,引用Microsoft.CodeAnalysis.CSharp和Microsoft.CodeAnalysis.Analyzers包,然后编写生成器核心逻辑:
核心逻辑分为三步:
- 扫描所有带
[TsInterface]特性的Hub类 - 提取类名、公共方法的元信息
- 按模板生成三个TS接口
以下是生成器的核心代码片段:
[Generator] public class TsInterfaceGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { // 注册特性接收器,捕获带[TsInterface]的类 context.RegisterForSyntaxNotifications(() => new TsInterfaceSyntaxReceiver()); } public void Execute(GeneratorExecutionContext context) { var receiver = context.SyntaxReceiver as TsInterfaceSyntaxReceiver; if (receiver?.TargetClasses == null) return; foreach (var classSymbol in receiver.TargetClasses) { var className = classSymbol.Name; var serverMethods = GetServerMethods(classSymbol); // 拼接生成TS代码字符串 var tsCode = $@" interface {className} extends SignalR.Hub.Proxy {{ client: {className}Client; server: {className}Server; }} interface {className}Client {{ // always empty. }} interface {className}Server {{ {string.Join("\n ", serverMethods)} }}"; // 将生成的代码添加到编译上下文,输出为.d.ts文件 context.AddSource($"{className}.d.ts", SourceText.From(tsCode, Encoding.UTF8)); } } // 提取Hub类的公共方法,转换为TS方法签名 private IEnumerable<string> GetServerMethods(INamedTypeSymbol classSymbol) { return classSymbol.GetMembers() .OfType<IMethodSymbol>() .Where(m => m.DeclaredAccessibility == Accessibility.Public && !m.IsStatic) .Select(ConvertToTsMethod); } // 将C#方法转换为符合TS风格的方法签名 private string ConvertToTsMethod(IMethodSymbol method) { // 处理返回值:Task<T> → Promise<T>,Task → Promise<void> var returnType = method.ReturnType.Name switch { "Task" => "Promise<void>", "Task`1" => $"Promise<{ConvertTsType(method.ReturnType.TypeArguments[0])}>", _ => "Promise<any>" }; // 处理参数:C#大驼峰参数名转小驼峰,加上TS类型 var parameters = method.Parameters.Select(p => $"{ToCamelCase(p.Name)}: {ConvertTsType(p.Type)}"); // 方法名转小驼峰(符合TS编码风格) var methodName = ToCamelCase(method.Name); return $"{methodName}({string.Join(", ", parameters)}): {returnType};"; } // C#类型到TS类型的基础映射(可按需扩展更多类型) private string ConvertTsType(ITypeSymbol type) { return type.Name switch { "String" => "string", "Int32" => "number", "IEnumerable`1" => $"{ConvertTsType(type.TypeArguments[0])}[]", // 可添加更多类型映射,比如DateTime转string等 _ => "any" }; } // 大驼峰转小驼峰工具方法 private string ToCamelCase(string input) { if (string.IsNullOrEmpty(input)) return input; return char.ToLowerInvariant(input[0]) + input.Substring(1); } } // 语法接收器,收集带[TsInterface]特性的类节点 public class TsInterfaceSyntaxReceiver : ISyntaxReceiver { public List<INamedTypeSymbol> TargetClasses { get; } = new(); public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { if (syntaxNode is ClassDeclarationSyntax classDecl && classDecl.AttributeLists.Any(al => al.Attributes.Any(a => a.Name.ToString() == "TsInterface"))) { // 在Execute阶段通过语义模型转换为INamedTypeSymbol,这里先标记需要处理的类 } } }
3. 使用生成器
将这个生成器项目作为依赖引用到你的SignalR项目中,给ChatHub加上[TsInterface]特性后编译项目,就能自动生成对应的.d.ts文件,内容和你期望的完全一致:
interface ChatHub extends SignalR.Hub.Proxy { client: ChatHubClient; server: ChatHubServer; } interface ChatHubClient { // always empty. } interface ChatHubServer { getMessages(): Promise<string[]>; emitMessage(msg: string): Promise<void>; }
补充说明
- 如果你不想自己编写生成器,也可以寻找现成的NuGet包(比如
SignalR.TypeScript类库),但自定义生成器能完全把控输出格式,更贴合你的个性化需求。 - 上面的类型映射是基础版本,你可以根据业务需求扩展更多类型转换规则,比如处理自定义实体类、枚举等。
内容的提问来源于stack exchange,提问作者S.Marius
相关产品推荐
相关产品推荐

