Search code examples
c#typescriptcode-translationreinforced-typings

Reinforced.Typings does not produce FQN for types with UseModules(true, false)


Summary:

I have a simple solution with two projects, e.g. ProjA and ProjB. In ProjA I have specified my custom the RTConfigure method for fluent configuration of the translation process using the Reinforeced.Typings tool for translating C# to TypeScript code. In ProjB I defined C# ViewModel classes that are intended for translation.

Problem:

In ProjB I defined three classes in three separate files and each of them belongs to their specific namespace. When using a global builder config by calling e.g. builder.UseModules(true, false) the produced code does not include fully qualified namespace names for the translated types that refer to types after the keyword extends and for types that are specified as members of another class (which belongs to another namespace different from the one of the used member type).

For example:

  1. ProjB.Ns1.Class1
  2. ProjB.Ns2.Class2
  3. ProjB.Ns3.Class3

All three classes defines just two simple props, where Class3 differs a bit by containing one extra property with the type of Class1. Class2 inherits from Class1.

Here is the example code:

// Class1.cs
namespace ReinforcedTypings.DifferentNamespaces.Ns1
{
  public class Class1
  {
    public int AnIntegerPropNs1 { get; set; }
    public string AStringPropNs1 { get; set; }
  }
}

// Class2.cs
using ReinforcedTypings.DifferentNamespaces.Ns1

namespace ReinforcedTypings.DifferentNamespaces.Ns2
{
  public class Class2 : Class1
  {
    public int AnIntegerPropNs2 { get; set; }
    public string AStringPropNs2 { get; set; }
  }
}

// Class3.cs
using ReinforcedTypings.DifferentNamespaces.Ns1

namespace ReinforcedTypings.DifferentNamespaces.Ns3
{
  public class Class3
  {
    public int AnIntegerPropNs3 { get; set; }
    public string AStringPropNs3 { get; set; }
    public Class1 AClass1PropNs3 { get; set; }
  }
}

The configure method is very simple and is defined as follows:

  public static class RTConfig
  {
    public static void Configure(ConfigurationBuilder builder)
    {
      var types = typeof(ReinforcedTypings.DifferentNamespaces.Ns1.Class1).Assembly.GetTypes().ToList();

      builder.ExportAsClasses(types.Where(x => x.IsClass), c => c.WithAllFields().WithAllMethods().WithAllProperties());
      builder.Global(c => c.UseModules(true, false));
      // ^ commenting out this line will also cause a different error
    }
  }

I use the .xml configuration file to specify that the translation should all result in one file by setting the following option (false) as:

<RtDivideTypesAmongFiles>false</RtDivideTypesAmongFiles>

...and this results in the following TypeScript code in (translated.ts as set in the .xml configuration file):

export namespace ReinforcedTypings.DifferentNamespaces.Ns3 {
    export class Class3
    {
        public AnIntegerPropNs3: number;
        public AStringPropNs3: string;
        public AClass1PropNs3: Class1; // <- error here
    }
}
export namespace ReinforcedTypings.DifferentNamespaces.Ns2 {
    export class Class2 extends Class1  // <- error here
    {
        public AnIntegerPropNs2: number;
        public AStringPropNs2: string;
    }
}
export namespace ReinforcedTypings.DifferentNamespaces.Ns1 {
    export class Class1
    {
        public AnIntegerPropNs1: number;
        public AStringPropNs1: string;
    }
}

Please notice that Class1 type is missing the full namespace notation for its name after extends and in the translated property member private AClass1PropNs3: Class1; of the Class3.

This causes an issue in TypeScript as these types are not found due to not specifying the full (namespace) path name of the type.

Side issue observed:

When not working with UseModules, if you comment out that line, another issue will be found in the translated TypeScript code and it is due to the order of translated code, i.e. the Class1 will not be found in Class3 because it is used before its declaration.

Conclusion:

I did not go into the source of RT but I doubt there is an one issue with the UseModules(true, false) where second argument explicitly states not to discard the namespaces and another issue regarding the order of the code that gets outputted. Anyways, having the second argument value set to false one would expect to always and everywhere have the FQN for the used type. If I am mistaken of the usage of this config prop, then I propose it would be of great benefit to have such global config which would enforce the usage of FQN for all/desired types.


Solution

  • Definitely FQN issue is bug of RT and is fixed in version 1.4.6.

    About side issue: see this GitHub Discussion