From 035237946286222a6e5c681198fc89272c611e9e Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sat, 28 Feb 2026 17:12:54 +0100 Subject: [PATCH 1/3] add failing test --- .../TypescriptAssemblyExportsRendererTests.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/TypeShim.Generator.Tests/TypeScript/TypescriptAssemblyExportsRendererTests.cs b/src/TypeShim.Generator.Tests/TypeScript/TypescriptAssemblyExportsRendererTests.cs index 409ff97a..b883b4c7 100644 --- a/src/TypeShim.Generator.Tests/TypeScript/TypescriptAssemblyExportsRendererTests.cs +++ b/src/TypeShim.Generator.Tests/TypeScript/TypescriptAssemblyExportsRendererTests.cs @@ -902,4 +902,57 @@ export interface AssemblyExports{ """.Replace("{{tsArrayType}}", tsArrayType)); } + + [Test] + public void TypeScriptInteropInterfaceRenderer_Constructor_WithUserClassWithRequiredPropertyParameterType_HasManagedObjectOrObjectType() + { + SyntaxTree userClass = CSharpSyntaxTree.ParseText(""" + using System; + using System.Threading.Tasks; + namespace N1; + [TSExport] + public class UserClass + { + public int Id { get; init; } + } + """); + + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(""" + using System; + using System.Threading.Tasks; + namespace N1; + [TSExport] + public class C1(UserClass uc) + { + public void DoStuff(UserClass u) {} + } + """); + + SymbolExtractor symbolExtractor = new([CSharpFileInfo.Create(syntaxTree), CSharpFileInfo.Create(userClass)], TestFixture.TargetingPackRefDir); + List exportedClasses = [.. symbolExtractor.ExtractAllExportedSymbols()]; + Assert.That(exportedClasses, Has.Count.EqualTo(2)); + INamedTypeSymbol classSymbol = exportedClasses[0]; + INamedTypeSymbol userClassSymbol = exportedClasses[1]; + + InteropTypeInfoCache typeCache = new(); + ClassInfo classInfo = new ClassInfoBuilder(classSymbol, typeCache).Build(); + ClassInfo userClassInfo = new ClassInfoBuilder(userClassSymbol, typeCache).Build(); + + ModuleHierarchyInfo hierarchyInfo = ModuleHierarchyInfo.FromClasses([classInfo]); //deliberate omit userClassInfo to reduce noise in baseline + RenderContext renderCtx = new(null, [classInfo, userClassInfo], RenderOptions.TypeScript); + new TypescriptAssemblyExportsRenderer(hierarchyInfo, renderCtx).Render(); + + AssertEx.EqualOrDiff(renderCtx.ToString(), """ +// TypeShim generated TypeScript module exports interface +export interface AssemblyExports{ + N1: { + C1Interop: { + ctor(uc: ManagedObject | object): ManagedObject; + DoStuff(instance: ManagedObject, u: ManagedObject | object): void; + }; + }; +} + +"""); + } } From 734bd0da3886746ed667b0f7f6ab4806332cb0b8 Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sat, 28 Feb 2026 17:13:30 +0100 Subject: [PATCH 2/3] preserve type info by dropping interop conversion --- .../Typescript/TypescriptAssemblyExportsRenderer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TypeShim.Generator/Typescript/TypescriptAssemblyExportsRenderer.cs b/src/TypeShim.Generator/Typescript/TypescriptAssemblyExportsRenderer.cs index c0b39aec..379bf776 100644 --- a/src/TypeShim.Generator/Typescript/TypescriptAssemblyExportsRenderer.cs +++ b/src/TypeShim.Generator/Typescript/TypescriptAssemblyExportsRenderer.cs @@ -46,7 +46,6 @@ private void RenderClassInteropMethods(ClassInfo classInfo) { if (classInfo.Constructor is ConstructorInfo constructorInfo) { - constructorInfo = constructorInfo.WithInteropTypeInfo(); RenderInteropMethodSignature(constructorInfo.Name, constructorInfo.GetParametersIncludingInitializerObject(), constructorInfo.Type); } foreach (MethodInfo methodInfo in GetAllMethods(classInfo)) From 96e9f61b1e27c56603600c5f28ae2185d5bb3898 Mon Sep 17 00:00:00 2001 From: Marc Zwart Date: Sat, 28 Feb 2026 17:14:02 +0100 Subject: [PATCH 3/3] remove obsolete interoptypeinfo methods --- .../CSharp/CSharpTypeConversionRenderer.cs | 2 +- src/TypeShim.Generator/Parsing/ConstructorInfo.cs | 12 ------------ src/TypeShim.Generator/Parsing/MethodInfo.cs | 12 ------------ .../Parsing/MethodParameterInfo.cs | 10 ---------- src/TypeShim.Generator/Parsing/PropertyInfo.cs | 15 --------------- .../Typescript/TypeScriptMethodRenderer.cs | 2 +- 6 files changed, 2 insertions(+), 51 deletions(-) diff --git a/src/TypeShim.Generator/CSharp/CSharpTypeConversionRenderer.cs b/src/TypeShim.Generator/CSharp/CSharpTypeConversionRenderer.cs index 53e5d263..d8e512cd 100644 --- a/src/TypeShim.Generator/CSharp/CSharpTypeConversionRenderer.cs +++ b/src/TypeShim.Generator/CSharp/CSharpTypeConversionRenderer.cs @@ -58,7 +58,7 @@ private DeferredExpressionRenderer RenderTypeDownConversion(InteropTypeInfo type internal DeferredExpressionRenderer RenderReturnTypeConversion(InteropTypeInfo returnType, DeferredExpressionRenderer valueExpressionRenderer) { if (!returnType.RequiresTypeConversion) - return valueExpressionRenderer; // DeferredExpressionRenderer.From(() => _ctx.Append(valueExpression)); + return valueExpressionRenderer; if (returnType is { IsTaskType: true, TypeArgument.RequiresTypeConversion: true }) // Handle Task { diff --git a/src/TypeShim.Generator/Parsing/ConstructorInfo.cs b/src/TypeShim.Generator/Parsing/ConstructorInfo.cs index de01d6bb..086bec6f 100644 --- a/src/TypeShim.Generator/Parsing/ConstructorInfo.cs +++ b/src/TypeShim.Generator/Parsing/ConstructorInfo.cs @@ -19,16 +19,4 @@ internal MethodParameterInfo[] GetParametersIncludingInitializerObject() } return Parameters; } - - internal ConstructorInfo WithInteropTypeInfo() - { - return new ConstructorInfo() - { - Name = Name, - Parameters = [.. Parameters.Select(p => p.WithInteropTypeInfo())], - InitializerObject = InitializerObject?.WithInteropTypeInfo(), - MemberInitializers = [.. MemberInitializers.Select(p => p.WithInteropTypeInfo())], - Type = Type.AsInteropTypeInfo(), - }; - } } diff --git a/src/TypeShim.Generator/Parsing/MethodInfo.cs b/src/TypeShim.Generator/Parsing/MethodInfo.cs index 68efcf76..16f95d02 100644 --- a/src/TypeShim.Generator/Parsing/MethodInfo.cs +++ b/src/TypeShim.Generator/Parsing/MethodInfo.cs @@ -23,18 +23,6 @@ public MethodInfo WithoutInstanceParameter() }; } - public MethodInfo WithInteropTypeInfo() - { - return new MethodInfo - { - IsStatic = this.IsStatic, - Name = this.Name, - Parameters = [.. this.Parameters.Select(p => p.WithInteropTypeInfo())], - ReturnType = this.ReturnType.AsInteropTypeInfo(), - Comment = this.Comment, - }; - } - internal bool MatchesDisposeSignature() { return Name == "Dispose" && !Parameters.Any(p => !p.IsInjectedInstanceParameter) && ReturnType.ManagedType == KnownManagedType.Void; diff --git a/src/TypeShim.Generator/Parsing/MethodParameterInfo.cs b/src/TypeShim.Generator/Parsing/MethodParameterInfo.cs index 4917e47e..ca732d26 100644 --- a/src/TypeShim.Generator/Parsing/MethodParameterInfo.cs +++ b/src/TypeShim.Generator/Parsing/MethodParameterInfo.cs @@ -5,14 +5,4 @@ internal class MethodParameterInfo internal required string Name { get; init; } internal required bool IsInjectedInstanceParameter { get; init; } internal required InteropTypeInfo Type { get; init; } - - internal MethodParameterInfo WithInteropTypeInfo() - { - return new MethodParameterInfo - { - Name = this.Name, - IsInjectedInstanceParameter = this.IsInjectedInstanceParameter, - Type = Type.AsInteropTypeInfo(), - }; - } } diff --git a/src/TypeShim.Generator/Parsing/PropertyInfo.cs b/src/TypeShim.Generator/Parsing/PropertyInfo.cs index 424aeb86..d313f9c4 100644 --- a/src/TypeShim.Generator/Parsing/PropertyInfo.cs +++ b/src/TypeShim.Generator/Parsing/PropertyInfo.cs @@ -13,19 +13,4 @@ internal sealed class PropertyInfo internal required MethodInfo? InitMethod { get; init; } internal required CommentInfo? Comment { get; init; } - - public PropertyInfo WithInteropTypeInfo() - { - return new PropertyInfo - { - Name = this.Name, - IsStatic = this.IsStatic, - IsRequired = this.IsRequired, - Type = this.Type.AsInteropTypeInfo(), - GetMethod = this.GetMethod.WithInteropTypeInfo(), - SetMethod = this.SetMethod?.WithInteropTypeInfo(), - InitMethod = this.InitMethod?.WithInteropTypeInfo(), - Comment = this.Comment, - }; - } } \ No newline at end of file diff --git a/src/TypeShim.Generator/Typescript/TypeScriptMethodRenderer.cs b/src/TypeShim.Generator/Typescript/TypeScriptMethodRenderer.cs index 8d8ceee4..29c4753b 100644 --- a/src/TypeShim.Generator/Typescript/TypeScriptMethodRenderer.cs +++ b/src/TypeShim.Generator/Typescript/TypeScriptMethodRenderer.cs @@ -120,7 +120,7 @@ private void RenderParameterList(IEnumerable parameterInfos ctx.Append(parameterInfo.Name).Append(": "); bool isDelegate = parameterInfo.Type.IsDelegateType() || (parameterInfo.Type.IsNullableType && parameterInfo.Type.TypeArgument!.IsDelegateType()); - TypeShimSymbolType returnSymbolType = parameterInfo.Type is { RequiresTypeConversion: true, SupportsTypeConversion: true } && !isDelegate + TypeShimSymbolType returnSymbolType = !isDelegate && parameterInfo.Type is { RequiresTypeConversion: true, SupportsTypeConversion: true } ? TypeShimSymbolType.ProxyInitializerUnion : TypeShimSymbolType.None; TypeScriptSymbolNameRenderer.Render(parameterInfo.Type, ctx, returnSymbolType, parameterSymbolType: TypeShimSymbolType.Proxy, interop: false);