Is it possible to produce typings for a C# class with both camel case and pascal case

Viewed 283

This is a strange requirement but we are moving away from pascal case with new TypeScript development but have some legacy code that is too difficult to change right now

So I'd like to have Reinforced Typings create an interface with both eg (probably will have to make all nullable)

export interface Foo {
    Name: string;
    name: string;
}
2 Answers

C# identifiers are case-sensitive, so Name and name are two different things.

In your case it looks like you are trying to migrate an API where the caller uses name, to use Name instead. If you need to keep name around temporarily, I would consider marking it obsolete with System.ObsoleteAttribute.

For example if you had a class:

public class Foo
{
    public string Name {get; set;}

    [Obsolete("This property is obsolete. Use Name instead.)]
    public string name { get { return Name; } set { Name = value; } }
}

Interfaces are a little more difficult as you will need to mark both the interface property and the class property as obsolete. See also Interface method marked as Obsolete does not issue a message from the compiler when implemented .

Your goal can be fullfilled with writing simple code generator and/or attribute. (Please update to version 1.4.1 if you already use fluent configuration).

Consider following attribute:

using Reinforced.Typings;
using Reinforced.Typings.Ast;
using Reinforced.Typings.Attributes;
using Reinforced.Typings.Fluent;
using Reinforced.Typings.Generators;

public class LegacySupportAttribute: TsPropertyAttribute
{
    public LegacySupportAttribute()
    {
        this.ShouldBeCamelCased = true;
        this.CodeGeneratorType = typeof(LegacyPropertyDuplicator);
    }
}

The LegacyPropertyDuplicator is RT's property code generator, written like that:

public class LegacyPropertyDuplicator : PropertyCodeGenerator
{
    /// <summary>
    ///     Main code generator method. This method should write corresponding TypeScript code for element (1st argument) to
    ///     WriterWrapper (3rd argument) using TypeResolver if necessary
    /// </summary>
    /// <param name="element">Element code to be generated to output</param>
    /// <param name="result">Resulting node</param>
    /// <param name="resolver">Type resolver</param>
    public override RtField GenerateNode(MemberInfo element, RtField result, TypeResolver resolver)
    {
        var b = base.GenerateNode(element, result, resolver);
        if (b == null) return null;

        var pascalCaseName = b.Identifier.IdentifierName.Substring(0, 1).ToUpperInvariant()
                             + b.Identifier.IdentifierName.Substring(1);

        var newField = new RtField()
        {
            AccessModifier = b.AccessModifier,
            Identifier = new RtIdentifier(pascalCaseName),
            InitializationExpression = b.InitializationExpression,
            IsStatic = b.IsStatic,
            Documentation = b.Documentation,
            Order = b.Order,
            Type = b.Type
        };
        if (Context.Location.CurrentClass != null) Context.Location.CurrentClass.Members.Add(newField);
        if (Context.Location.CurrentInterface != null) Context.Location.CurrentInterface.Members.Add(newField);
        return b;
    }
}

Then use this attribute like that:

public class TestClass
{
    [LegacySupport]
    public string LastName { get; set; }
}

And get following result:

export interface ITestClass
{
    lastName: string;
    LastName: string;
}

You also can specify code generator within fluent configuration:

public static void Configure(ConfigurationBuilder cb)
{
    cb.ExportAsInterface<TestClass>()
        .WithPublicProperties()
        .WithProperty(x => x.LastName, x => x.WithCodeGenerator<LegacyPropertyDuplicator>());
}
Related