Differences between C# code and TypeScript transpiled code

There are different behaviors between the C# code and the transpiled TypeScript code.

Depending on the case:

  • We make an evolution so that the behavior of the transpiled TypeScript code is the same as the C# code.
  • We do nothing because it has little added value and is too complex or it may cause performance issues.

On this page, you will find the cases of the second scenario. It will be updated as we encounter cases.

Null string interpolation

For the following C# code:

int? value = null;
string message = $"The value is {value}";

The transpiled TypeScript code is :

let value: number | null = null
let message: string = `The value is ${value}`

In C#, the "message" variable has the value "The value is ".

In TypeScript, the variable "message" has the value "The value is null".

Nullable numeric value parsing

In C#, the following parsing code produces an exception.

string? valueToParse = null;
decimal result = decimal.Parse(valueToParse);

return result;

However, when transpiled in TypeScript, this code will not throw and will return NaN.

When parsing values, it is recommended to check the result using the double.IsNan() method. You can use this method whether the C# value to check is a double, an int, a decimal or any other number type as they are all transpiled to the same number type in TypeScript.

string? valueToParse = null;
decimal result = decimal.Parse(valueToParse);

if (double.IsNaN((double)result))
{
    // Handle error case
}

return result;

Using statement

using declarations and statements are transpiled to a try/finally block calling Dispose. When a declaration has several declarators, they are disposed in reverse order. Only types whose Dispose method is transpiled can be used. await using and using (existingVariable) without a declaration are not supported. See Periodic processing.

Dictionary serialization to JSON

A Dictionary<TKey, TValue>, an IDictionary<TKey, TValue> and an IReadOnlyDictionary<TKey, TValue> are transpiled to a TypeScript Map. JsonSerializer.Serialize is transpiled to JSON.stringify, which serializes a Map as an empty object {}.

To serialize the entries of a dictionary, convert it to a GroupeIsa.Neos.Designer.UIAbstractions.Collections.Record<TKey, TValue>, transpiled to a TypeScript Record, a plain object. To read a JSON object back, cast the result of JsonSerializer.Deserialize to a Record, then call ToDictionary() to get a dictionary. Write the full name of the type: its namespace is not imported by default in UI code, so that Record never conflicts with a type of the same name in a cluster.

Dictionary<string, object?> parameters = new() { ["Year"] = 2026 };
string? json = JsonSerializer.Serialize(new GroupeIsa.Neos.Designer.UIAbstractions.Collections.Record<string, object?>(parameters));

var record = (GroupeIsa.Neos.Designer.UIAbstractions.Collections.Record<string, object?>?)JsonSerializer.Deserialize(json!);
Dictionary<string, object?>? values = record?.ToDictionary();

A Record is not a dictionary:

  • only string and int keys are supported, because the keys of a JavaScript object are strings,
  • it does not implement IReadOnlyDictionary<TKey, TValue> and cannot be enumerated with foreach: call ToDictionary() (for the keys, ToDictionary().Keys) or Values,
  • JSON does not keep the types: a DateTime value comes back as a string, to be converted by the code that reads it.

Known unsupported C# syntaxes

The following list is not exhaustive.