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

"Class used before its declaration" in the generated file #58

Closed
yegorpetrov opened this issue Dec 15, 2017 · 8 comments
Closed

"Class used before its declaration" in the generated file #58

yegorpetrov opened this issue Dec 15, 2017 · 8 comments
Labels

Comments

@yegorpetrov
Copy link

Basically, this:

image

I'm not sure if it's a bug, but I guess it shouldn't happen at least in the default configuration.

Here is the example from the screenshot:
reinforced_class_used_before_its_declaration.zip

@pavel-b-novikov
Copy link
Member

pavel-b-novikov commented Dec 15, 2017

Why dont you use builder.ExportAs...().Order(10) method or .ExportAsClasses(...,x=>x.Order(...))?
Please ask questions on StackOverflow

@yegorpetrov
Copy link
Author

yegorpetrov commented Dec 15, 2017

The problem I see here is that the output is erroneous by default. And it can be cumbersome to figure out and maintain the proper order in a situation with numerous interdependent classes. I kinda expected this framework to order types properly by itself.

Is this out of the scope of the project? Were there attempts to sort it out automatically?

@pavel-b-novikov
Copy link
Member

pavel-b-novikov commented Dec 15, 2017

It is out of scope of project because

  • this case is complex to detect and resolve when exporting to multiple files/namespaces plus inheritance of classes in different namespaces plus how it should work with TS modules?
  • this case is extremely rare and easily fixable by pulling all the base classes to the top using .Order
  • so according to previous 2 points, ratio of time spent implementing this feature/collected benefits is extremely low

So for now I'm not going to implement that. If you want to implement this feature - feel free to fork & PR. Otherwise please use .Order

@yegorpetrov
Copy link
Author

yegorpetrov commented Dec 18, 2017

If someone else comes upon this issue, here's one way to "pull all the base classes to the top" in a more or less universal manner:

IEnumerable<T> EnumerateHierarchy<T>(T item, Func<T, T> selector)
{
    do yield return item;
    while ((item = selector(item)) != default(T));
}
...
IEnumerable<Type> dtoClasses...
Func<Type, int> orderFunc = dtoClasses.SelectMany(
    t => EnumerateHierarchy(t, e => e.BaseType).Reverse()).ToList().IndexOf;
cfg.ExportAsClasses(dtoClasses, c => c.Order(orderFunc(c.Type))...

May be quite far from perfect, it's only tested to cover my specific case.

@pavel-b-novikov
Copy link
Member

pavel-b-novikov commented Dec 18, 2017

Thank you, Egor! But it would be much better if you post such kind of question to stackoverflow - more audience coverage there.

I have marked your solution with "faq" label, but still not sure that someone can find it quickly.

@vmandic
Copy link

vmandic commented Feb 14, 2018

Thanks for the idea @yegorpetrov, the proposed solution is OK but the c.Order(orderFunc(c.Type)) call does not work as expected (I am using the latest tool version of 1.4.6), i.e. the reflected/translated code is not in the order it should be...

I made a bit more verbose version of the hierarchy enumeration with a Type dict like so:

    public static void Configure(ConfigurationBuilder builder)
    {
      var types = typeof(Class1).Assembly.GetTypes().ToList();

      var orderedTypes = new Dictionary<Type, List<Type>>();
      types.ForEach(t => orderedTypes.Add(t, EnumerateHierarchy(t, e => e.BaseType).Reverse().ToList()));
      builder.ExportAsClasses(types, c => c.Order(orderedTypes[c.Type].IndexOf(c.Type)));
    }

    static IEnumerable<T> EnumerateHierarchy<T>(T item, Func<T, T> selector) where T : class
    {
      do
      {
        yield return item;
        item = selector(item);
      }
      while (item != default(T));
    }

... I tested this separately on three class files (Class1, Class2, Class3) from three different Namespaces like so:

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

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

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

And the tested order I get is:

  • Class 3 with order number 1
  • Class 2 with order number 2
  • Class 1 with order number 1

So either way the Class1 should appear before Class2 which extends it in TypeScript so we shouldn't get the error of having a class used before its declaration, but unfortunately that is not the case.

The output:

image

@yegorpetrov
Copy link
Author

yegorpetrov commented Feb 14, 2018

@vmandic I think that's because you have separate namespaces. You can't expect the lower level order to override the higher level one automatically.

@pavel-b-novikov
Copy link
Member

RT. Connecting people.

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

No branches or pull requests

3 participants