Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type generation with nullability fails #147

Closed
rijulg opened this issue Jun 25, 2023 · 2 comments · Fixed by #158
Closed

Type generation with nullability fails #147

rijulg opened this issue Jun 25, 2023 · 2 comments · Fixed by #158
Assignees

Comments

@rijulg
Copy link
Contributor

rijulg commented Jun 25, 2023

Brief explanation

Having multiple optional and required properties on a class results in unexpected nullability info in the generated types.
Issue on commit id: 27d7f9e

Minimal Example

The following dotnet module generated the corresponding types as shown

node node-api-dotnet-generator --assembly dotnet.dll --typedefs types.d.ts
// dotnet.dll
namespace ExampleSpace
{
  public class Model1
  {
    public DateTime requiredDate { get; set; }
    public string? optionalString { get; set; }
  }
  public class Model2
  {
    public DateTime requiredDate { get; set; }
    
    public DateTime? optionalDate { get; set; }
  }
  public class Model3
  {
    public DateTime requiredDate { get; set; }
    public string requiredString { get; set; }
    public string? optionalString { get; set; }
  }
}
// types.d.ts
declare module 'node-api-dotnet' {

	export namespace ExampleSpace {
		export class Model1 {
			constructor();

			requiredDate?: Date;

			optionalString?: string;
		}
	}

	export namespace ExampleSpace {
		export class Model2 {
			constructor();

			requiredDate: Date;

			optionalDate?: Date;
		}
	}

	export namespace ExampleSpace {
		export class Model3 {
			constructor();

			requiredDate: Date;

			requiredString: string;

			optionalString?: string;
		}
	}
}

Further Investigation

I have investigated the issue rather thoroughly and implemented the following reflection method to test where the issue is coming from

    static void MyReflect(Assembly assembly) {
        Console.WriteLine($"Reflecting Assembly: {assembly.FullName}");
        NullabilityInfoContext _nullability = new();
        foreach(Type type in assembly.GetTypes()) {
            Console.WriteLine($"{type}{{");
            foreach (PropertyInfo property in type.GetProperties()) {
                var nullability = _nullability.Create(property);
                Console.WriteLine($"\tname: {property.Name}");
                Console.WriteLine($"\ttype: {property.GetType()}");
                Console.WriteLine($"\tpropertyType: {property.PropertyType}");
                Console.WriteLine($"\treflectedType: {property.ReflectedType}");
                Console.WriteLine($"\t\tnullability: ");
                Console.WriteLine($"\t\tReadState: {nullability.ReadState}");
                Console.WriteLine($"\t\tWriteState: {nullability.WriteState}");
            }
            Console.WriteLine("}");
        }
    }

Within TypeDefinitionsGenerator.cs the assembly is loaded using an assemblyResolver; To pinpoint the issue I call MyReflect as following:

        Assembly assembly = loadContext.LoadFromAssemblyPath(assemblyPath);
        MyReflect(assembly);
        MyReflect(Assembly.LoadFrom(assemblyPath));

Which provides the following information (note the output has been cut down)

  1. Calling assembly loaded by assemblyResolver

ExampleSpace.Model1{
        name: requiredDate
        type: System.Reflection.TypeLoading.Ecma.EcmaProperty
        propertyType: System.DateTime
        reflectedType: ExampleSpace.Model1
                nullability: 
                ReadState: Nullable
                WriteState: Nullable
        name: optionalString
        type: System.Reflection.TypeLoading.Ecma.EcmaProperty
        propertyType: System.String
        reflectedType: ExampleSpace.Model1
                nullability: 
                ReadState: Nullable
                WriteState: Nullable
}
  1. Calling assembly loaded directly
ExampleSpace.Model1{
        name: requiredDate
        type: System.Reflection.RuntimePropertyInfo
        propertyType: System.DateTime
        reflectedType: ExampleSpace.Model1
                nullability: 
                ReadState: NotNull
                WriteState: NotNull
        name: optionalString
        type: System.Reflection.RuntimePropertyInfo
        propertyType: System.String
        reflectedType: ExampleSpace.Model1
                nullability: 
                ReadState: Nullable
                WriteState: Nullable
}

Hypothesis

Based on the observations I believe that the Ecma Module is messing with the nullability information somehow; I haven't worked with that so far so I am not sure what within that module may be causing the issue. If there are any ideas, then I will be happy to investigate and provide a fix if and when I get time in future.

@jasongin jasongin self-assigned this Sep 23, 2023
@jasongin
Copy link
Member

I'll investigate.

@jasongin
Copy link
Member

Fixed in #158

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants