Table of Contents

Transpiler C# to Typescript

Symbols transpilation

YAML file format

Example: transpilation of the method Round of string System.Math :

- CSharpMember: M
  CSharpSymbol: "Round(System.Double, System.Int32, System.MidpointRounding)"
  Infos: "Static - System.Double"
  TsTemplate: "round({0}, {1}, {2})"
  Dependencies:
    - Module: "@neos/shared"
      Imports:
        - round
        - MidpointRounding
  Tests:
    - Cs: "return Math.Round(2.34d, 1, MidpointRounding.ToEven);"
      Ts: "return round(2.34, 1, MidpointRounding.ToEven)"
Property Type Required Description
CSharpMember Enum Yes C# member of class : see csharpmember values
CsharpSymbol String Yes C# member signature : see member signature
Infos String No Information on CSharpMember, indicate if the member is static or not and the return type of the method or the property type. This property must not be manually modified.
TsTemplate String No Contains the typescript template result of the C# transpilation : see typescript template
TsTemplateSetter String No Contains the typescript template for a setter property if different of a getter property. If setter or getter property transpilation is the same, TsTemplateSetter should not be filled. {value} represents the transpilation of the value expression.
Dependencies Array No Allows to indicate the dependency modules and exports needed for the transpilation.
Dependencies.Module String Yes module path
Dependencies.Imports Array (String) No Imports from module needed for the transpilation, ex: import {round, MidpointRounding} from '@neos/shared'
Dependencies.DefaultExport String No Default Import from module, ex : import moment from 'moment'
Tests Array No List of pairs of C# code and Typescript to create unit tests that compare that transpiled C# generate expected typescript. Execution of C# code and typescript will be also compared in the unit test.
Tests.Cs String Yes C# code snippet, body of a C# method returning the return type of the transpiled method or property.
Tests.Ts String Yes Typescript code snippet that must match C# transpiled code.
Tests.ReturnType String No C# type of test return. If the field is empty, the expected return is the type of membership of the test member.
ExpectedValue String No Expected javascript value when running the test. In this case the C# snippet is not executed, the test consists only in the comparison of the executed javascript value and this value.
GenerateAutoTest Boolean No This attribute is true by default, if it is false the self-tests are not generated. It can be used when it is not possible to generate tests on instance symbols for which it is not possible to automatically create an instance.

CSharpMember Values

Value Description
T Type
C Constuctor
P Property
M Method
I Indexer
L Literal
This Accessor this
Default Default Value

Member signature

  • Type
    Full name of the type.
    Example of reference type : System.String
    Example of generic type : System.Collections.Generic.Dictionary<TKey, TValue>

  • Method
    Name of the method with it's arguments and modifiers.
    Example with params : Concat(params System.Object[])
    Example with optional parameter : Split(System.Char, [System.StringSplitOptions])
    Example with out parameter :TryParse(System.ReadOnlySpan<System.Char>, out System.Int32)

  • Constructor
    Name of the type with it's arguments and modifiers.
    Example for C# type System.Collections.Generic.List<T> : List(System.Collections.Generic.IEnumerable<T>)
    Example for C# type System.DateTime : DateTime()

  • Property
    Name of the property.

  • Indexer The name of indexer is this keyword. Example for C# type System.StringBuilder : this[System.Int32]

  • Literal
    Code of an example of C# literal for this type.
    For example "\"ABC\"" can be the csharpsymbol literal for string type.
    This literal and its template typescript are used for the automatic creation of snippets for unit tests.

  • This This type of member does not have a signature.

  • Default Indicates the default value of the type. This value is used when creating script type initializers on this type.

    Example for System.Int32

    - CSharpMember: Default
    TsTemplate: "0"
    

Typescript template syntax

The typescript template is typescript code.
Dynamic values from C # code are accessible via a special syntax between curly braces.
To escape a curly brace, it must be doubled.

Value Description
{0} Replaced by transpilation of the first method argument
{1} Replaced by transpilation of the second method argument and so on for other integers
{0-} Replace a C# params array to all arguments from the position into a comma separated list of arguments
{this} Replaced by transpilation of the expression on which the instance member is called
{T} For generic type or method : Replaced by the transpiled type of generic type parameter named T.
{value} In a TsTemplateSetter only. Replaced by the transpilation of the value expression of a property entry.
{Interface} In a constructor template only. Indicates that the C# class is transpiled in a typescript interface

Type template

The type template represents the equivalent type in javascript.

Example for System.Int32 :

CSharpMember: T
CSharpSymbol: "System.Int32"
TsTemplate: "number"

So the following C# code is transpiled into Javascript :

int i;
let i: number;

Example for System.Collections.Generic.List :

CSharpMember: T
CSharpSymbol: "System.Collections.Generic.List<T>"
TsTemplate: "{T}[]"

So the following C# code is transpiled into Javascript :

List<string> list;
let list: string[];

Constructor template

The constructor template represents the construction of an instance of the type. If the new keyword is necessary it must be part of the template.

Example with new keyword :

CSharpSymbol: DateTime(System.Int32, System.Int32, System.Int32)
TsTemplate: new Date({0}, {1} - 1, {2})

Example without new keyword :

CSharpSymbol: DateTime()
TsTemplate: moment.utc({{y:1, M:0, d:1}}).toDate()

For the particular case of a C# class that must be transpiled into a typescript interface, the template of the constructor must be {Interface}.

Example : ApiGetAllRequest is transpiled into interface.

- CSharpMember: C
  CSharpSymbol: "ApiGetAllRequest()"
  TsTemplate: "{Interface}"
  Dependencies:
  Tests:
- CSharpMember: P
  CSharpSymbol: "PageNumber"
  Infos: "Instance - System.Nullable<System.Int32>"
  TsTemplate: "pageNumber"

The transpilation of the C# code below

var item = new ApiGetAllRequest{ Path = "APIPath", PageNumber = 0,  RecordsByPage = 10};

provides the model code below

let item = { path: "APIPath", pageNumber: 0, recordsByPage: 10 };

Method template

The method template represents a method call.

Example of instance method with one parameter

CSharpSymbol: CompareTo(System.String)
TsTemplate: {this}.localeCompare({0})
Note

{this} must be indicated, it specifies the instance on which the method is called.

Example of static method with "params" parameters

CSharpSymbol: Concat(params System.Object[])
TsTemplate: [{0-}].join('')

Property template or Indexer template

The property template represents the reading or writing of a property.
If only TsTemplate is filled in, this template represents reading or writing.
TsTemplateSetter specifies a template for writing.

Example of instance property (System.String)

CSharpSymbol: Length
TsTemplate: {this}.length

Example of static property (System.DateTime)

CSharpSymbol: Now
TsTemplate: new Date()

Example of TsTemplateSetter

CSharpSymbol: Property
TsTemplate: {this}.property
TsTemplateSetter: {this}.setProperty({value})

Example of Indexer (System.StringBuilder)

CSharpSymbol: this[System.Int32]
TsTemplate: {this}[{0}]

Accessor This

This template represents the transpilation of the this keyword of the members or inherited members of this type when accessed in this type or in inherited types.

Example :

- CSharpMember: This
  TsTemplate: "this.owner"

YAML file generation

The console tool GroupeIsa.Neos.Transpiler.SymbolsGenerator generates or updates the symbol YAML files.
YAML file are generated in folder named Symbols in the C# project GroupeIsa.Neos.Transpiler.Symbols.

Caution

The Build Action of YAML file must be set as Embedded resource

The types to be generated are specified in the config file of the generator.

File SymbolGeneratorConfig.yml:

# The path where symbols YAML file will be generated.
SymbolsOutputPath: "../../../../GroupeIsa.Neos.Transpiler.Symbols/Symbols"

# The path where C# snippets classes will be generated.
CSharpSnippetsOutputPath: "../../../../GroupeIsa.Neos.Transpiler.Tests.CSharpSnippets/Symbols"

# The path where unit tests classes will be generated.
UnitTestsOutputPath: "../../../../GroupeIsa.Neos.Transpiler.Snippets.Tests.UnitTests"

#Generates symbols YAML file for followed types
Types:
  - Name: GroupeIsa.Neos.Application.Rules.ValidationRule`1, GroupeIsa.Neos.Application
    GenerateProtected: true

  - GroupeIsa.Neos.Transpiler.Abstractions.API.SortDirection, GroupeIsa.Neos.Transpiler.Abstractions

The elements of the array Types are String or Object.

  • String element : Full name of C# type. If type is not in assemblies mscorlib.dll/System.Private.CoreLib.dll the assembly-qualified name of the type must be specified.
  • Object element :
    • Name (string): Name of C# type (see string element).
    • GenerateProtected (boolean) : A value indicating whether the symbols for protected members must be generated
Note

The generation of protected members is necessary e.g. for abstract classes of event rules.

Unit tests

Tests generation.

The console tool GroupeIsa.Neos.Transpiler.SymbolsGenerator generates or updates the unit tests and C# snippets according to YAML symbol files.

C# snippets are generated in C# project GroupeIsa.Neos.Transpiler.Tests.CSharpSnippets in the folder Symbols.

C# snippets is a static method that return a result. Above this method a special comment contains the expected Typescript. The comment is multi-lines and the first line of the comment must be Typescript.

Example for String.Length :

YAML symbol :

CSharpSymbol: Length
TsTemplate: {this}.length

Snippet :

/* Typescript
    let item = 'ABC'
    return item.length
*/
public static int PropertyLength()
{
    string item = "ABC";
    return item.Length;
}

A unit tests for this snippet is generated in folder SymbolsUnitTests of project GroupeIsa.Neos.Transpiler.Snippets.Tests.UnitTests.

/// <summary>
/// Tests the method Length.
/// </summary>
///<returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Fact]
public async Task TestPropertyLength()
{
    using TranspilerTester tester = new TranspilerTester(GetLogger<TranspilerTester>());
    await tester.RunTest(SnippetPath, "PropertyLength");
    Assert.True(true);
}

Integration tests

The integration test differs from the unit test.
The unit test transposes a C# code into typescript and compares the resulting code to the expected code.
The integration test also compiles the resulting typescript code and executes the code. The return value of the execution is then compared to the return value of the C# code execution.

Note

The test is executed in integration mode only if the environment variable TESTTRANSPILERCOMPARISON is set to 'Y'.

Prerequisites

  • NodeJs : version 18, download to NodeJs.

  • Tsc : To be installed globally with the following command

    npm install -g typescript
    

Under the hood

The typescript code is compiled in javascript via the tsc command.
The unit test fails if the typescript code does not compile.
Then the javascript code is executed via nodeJs, the return value serialized in Json is retrieved.