Skip to content
Pavel Novikov edited this page Jan 8, 2019 · 12 revisions

Reinforced.Typings automatically resolves your types according to following (more or less formal) rules:

Type Resolution rules

Simple types

  • (s)byte, (u)short, (u)int, (u)long, float,double,decimal => number
  • string, char => string
  • object => any
  • bool => boolean
  • void => void (did you know that typeof(void) is correct C# construction?)

Nullables

  • Nullables are resolved as they are not nullables. You can enable AutoOptionalProperties option in [TsGlobal] attribute or use corresponding fluent configuration method to make RT export properties of Nullable<> type as optional properties if you wish.

Both nullables and simple types will be resolved to RtSimpleTypeName AST.

Tuples (version 1.3.0)

  • Everything that is of System.Tuple<> type will be resolved to TypeScript tuples (RtTuple AST)

Dictionaries

  • Everything that is non-generic dictionary (implements System.Collections.IDictionary) will be resolved to { [key:any] :any }. Reinforced.Typings will produce warning in this case because TypeScript hashes can have only number or string as key. Try to avoid using nongeneric Dictionaries
  • Everything that implements System.Collections.Generic.IDictionary<,>, System.Collections.Generic.IReadOnlyDictionary<,> or has type System.Collections.Generic.Dictionary<,> will be resolved as TypeScript hash { [key:TKey]:TValue } where both TKey and TValue will be resolved according to current rules. In case is TKey is not either number or string - warning will be produced

Dictionaries are resolved to RtDictionaryType AST.

Collections

  • Everything that implements System.Collections.IEnumerable (without generic parametrization) will be strictly resolved to any[] - array of any
  • Every array T[] will be resolved to TypeScript array T[] where array element type T will be resolved according to current rules
  • Everything that implements System.Collections.Generic.IEnumerable<T> will be resolved to TypeScript array T[] where array element type T will be resolved according to current rules

Collections are resolved to RtArrayType AST.

Delegates

  • Everythis that inherits System.MulticastDelegate will be resolved to TypeScript function type (a:T1,B:T2...) => TResult (RtDelegateType AST). Types of all parameters will be resolved according to current rules as well as return type. If delegate does not have return type - void will be assumed. Arguments will be named arg in case of single argument and arg1, arg2 etc in case of multiple arguments

Exported types

  • Exported type will be resolved to RtSimpleTypeName with respect to overriden name and generic parameters until it derives collection class/interface or dictionary class/interface. In this case rules of collections and dictionaries resolution will be applied

Generic parameters

  • If exported type is generic one then all generic parameters will be left in place and exported type will be generic
  • If some of generic parameters are materialized by context (e.g. property of generic type sharing generic parameter with declaring type) - they will be also left in place leaving type generic
  • If some of generic parameters are intentilnally materialized either with inheritance or using [TsGeneric] attribute - they will behave like usual C# materialized generics

Inheritance

  • If type A inherits type B and both are exported then inheritance will be exported to TypeScript without changes
  • If type A inherits type B and B is not exported then B will be removed from A's inheritance chain. So export your base types along with inheritors.
  • If type A inherits type B and B is exported as interface whether A is exported as class then Reinforced.Typings will by force implement all members from B in A, produce corresponding warning and leave @todo JSDOC comment in place where accident took place

Any

  • All other types are unknown for Reinforced.Typings and fall into TypeScript any type

Substitutions

You can intentionally instruct Reinforced.Typings which type AST node to resolve particular type to. This mechanism called "substitutions" and can be used only from fluent configuration due to technical limitations. Use .Substitute method of ConfigurationBuilder to define substitution:

    builder.Substitute(typeof(System.Guid), new RtSimpleTypeName("string"));

This configuration will strictly resolve System.Guid type to string wherever it appears among all the files. In the same way you can define substitution type-wide:

    builder.ExportAsInterface<MyType>()
        .Substitute(typeof(System.DateTime), new RtSimpleTypeName("Date"))
        .WithPublicProperties();

This configuration will strictly resolve System.DateTime to Date TypeScript type, but only within of MyType exporting. In other words, it will not touch DateTimes in other exported classes leaving them any.

You also can use other types from namespace Reinforced.Typings.Ast.TypeNames derived from RtTypeName (RtDelegateType,RtArrayType, RtDictionaryType,RtTuple). Please refer to XMLDOC for these types to properly instantiate them.

Related test

Inline Type Inferers

(since version 1.3.2)

There is also ability to override type inferer for particular property/field/method/parameter using inline type inferer. Use .InferType method that is available within property/field/method/parameter configuration. .InferType method presents in 4 variations. All variations have exactly one and only parameter - delegate that:

  • Consuming source member (MemberInfo/ParameterInfo/MethoInfo) and returns string. Result will be converted to simple type name
    • ...the same, but returning RtTypeName
  • Consuming source member as 1st parameter and TypeInferer as 2nd parameter, returns string. Result will be converted to simple type name. Use 2nd parameter (type inferer) to convert Type objects to RtTypeName
    • ... the same, but returning RtTypeName

Examples:

// results MyProperty : Observable<number>
builder.ExportAsInterface<MyType>()
    .WithProperty(x => x.MyProperty, x => x.InferType(_ => "Observable<string>"));
    
// the same, but in other words    
builder.ExportAsInterface<MyType>()
    .WithProperty(x => x.MyProperty, x => 
            new RtSimpleTypeName("Observable", new RtSimpleTypeName("string"))); 
            
// Wraps all public properties with Observable<>            
builder.ExportAsInterface<MyType>()    
    .WithPublicProperties(x => x.InferType(
            (m, t) => new RtSimpleTypeName("Observable", t.ResolveTypeName(((PropertyInfo)m).PropertyType))
        )
    );
    
// Wraps method parameter and return type with Observable<>
builder
    .WithMethod(x => x.SomeMethod3(
            Ts.Parameter<int>(t => t.InferType(
                    (m, r) => new RtSimpleTypeName("Observable", r.ResolveTypeName(m.ParameterType)))
                    )
               ),
               x => x.InferType((m, r) => new RtSimpleTypeName("Observable", r.ResolveTypeName(m.ReturnType)))
    );

Related test