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.