Type Mappings
How TypeScript types are converted to C# types.
Primitive Types
| TypeScript | C# | Notes |
|---|---|---|
number |
double |
64-bit floating point |
string |
string |
.NET System.String |
boolean |
bool |
.NET System.Boolean |
null |
null |
Nullable reference |
undefined |
null |
Maps to null |
void |
void |
No return value |
never |
N/A | Compile error |
any |
N/A | Not supported |
unknown |
object |
Base object type |
Explicit CLR Types
From @tsonic/core (numeric + other CLR primitives):
| TypeScript | C# | .NET Type |
|---|---|---|
int |
int |
System.Int32 |
float |
float |
System.Single |
long |
long |
System.Int64 |
byte |
byte |
System.Byte |
short |
short |
System.Int16 |
uint |
uint |
System.UInt32 |
ulong |
ulong |
System.UInt64 |
decimal |
decimal |
System.Decimal |
char |
char |
System.Char |
Array Types
Arrays emit as native C# arrays:
// TypeScript
const arr: number[] = [1, 2, 3];
const strings: Array<string> = ["a", "b"];
// C#
double[] arr = [1, 2, 3];
string[] strings = ["a", "b"];
Both T[] and Array<T> syntax emit as native arrays.
List for Dynamic Collections
Use List<T> when you need add/remove operations:
import { List } from "@tsonic/dotnet/System.Collections.Generic.js";
const list = new List<number>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Tuple Types
// TypeScript
const point: [number, number] = [10, 20];
const record: [string, number, boolean] = ["name", 42, true];
// C#
ValueTuple<double, double> point = (10, 20);
ValueTuple<string, double, bool> record = ("name", 42, true);
Tuples with 8+ elements use nested ValueTuple with TRest.
Dictionary and HashSet Types
Tsonic does not include JavaScript Map/Set in the default globals. Use .NET collections:
import { Dictionary, HashSet } from "@tsonic/dotnet/System.Collections.Generic.js";
// TypeScript
const map = new Dictionary<string, number>();
const set = new HashSet<number>();
// C#
var map = new Dictionary<string, double>();
var set = new HashSet<double>();
Generic Types
Type Parameters
// TypeScript
function identity<T>(x: T): T { return x; }
// C#
public static T identity<T>(T x) { return x; }
Generic Classes
// TypeScript
class Box<T> {
constructor(private value: T) {}
get(): T { return this.value; }
}
// C#
public class Box<T>
{
private T value;
public Box(T value) { this.value = value; }
public T get() { return value; }
}
Type Constraints
// TypeScript
interface HasId { id: number; }
function getId<T extends HasId>(item: T): number {
return item.id;
}
// C#
public static double getId<T>(T item) where T : HasId
{
return item.id;
}
Null in Generic Contexts
In generic contexts, null emits as default to handle both reference and value types:
// TypeScript
function orNull<T>(value: T): T | null {
return condition ? value : null;
}
// C#
public static T orNull<T>(T value)
{
return condition ? value : default;
}
Function Types
Function Signatures
// TypeScript
type Handler = (event: Event) => void;
// C#
// Func<Event, void> doesn't exist, so:
// Action<Event>
type Transform<T, U> = (input: T) => U;
// C#: Func<T, U>
Mapping Table
| TypeScript | C# |
|---|---|
() => void |
Action |
(a: T) => void |
Action<T> |
() => T |
Func<T> |
(a: T) => U |
Func<T, U> |
(a: T, b: U) => V |
Func<T, U, V> |
Object Types
Interfaces
// TypeScript
interface User {
id: number;
name: string;
email?: string;
}
// C#
public class User
{
public double id { get; set; }
public string name { get; set; }
public string? email { get; set; }
}
Anonymous Objects
Simple object literals auto-synthesize nominal types:
// TypeScript
const point = { x: 10, y: 20 };
// C# (synthesized class generated)
// class __Anon_File_Line_Col {
// public double x { get; set; }
// public double y { get; set; }
// }
var point = new __Anon_File_Line_Col { x = 10, y = 20 };
Method shorthand and getters/setters require explicit type annotation.
Union Types
Nullable Unions
// TypeScript
type MaybeString = string | null;
// C#
string?
Discriminated Unions
// TypeScript
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: string };
// C# (generates base class + variants)
public abstract class Result<T> { }
public class ResultOk<T> : Result<T>
{
public bool ok => true;
public T value { get; set; }
}
public class ResultError<T> : Result<T>
{
public bool ok => false;
public string error { get; set; }
}
General Unions
// TypeScript
type StringOrNumber = string | number;
// C# (fallback to object)
object;
Intersection Types
NOT SUPPORTED - Intersection types (A & B) cannot be compiled to C#.
// Error TSN7410
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged; // Not supported
Workaround: Create an explicit interface combining both types:
interface Person {
name: string;
age: number;
}
Literal Types
// TypeScript
type Direction = "north" | "south" | "east" | "west";
// C# (string constants or enum)
// Context-dependent
Enum Types
Numeric Enums
// TypeScript
enum Status {
Pending = 0,
Active = 1,
Complete = 2
}
// C#
public enum Status
{
Pending = 0,
Active = 1,
Complete = 2
}
String Enums
// TypeScript
enum Color {
Red = "red",
Green = "green"
}
// C# (class with constants)
public static class Color
{
public const string Red = "red";
public const string Green = "green";
}
CLR Type Mappings
When importing .NET types:
| Import | C# Type |
|---|---|
List<T> |
System.Collections.Generic.List<T> |
Dictionary<K,V> |
System.Collections.Generic.Dictionary<K,V> |
HashSet<T> |
System.Collections.Generic.HashSet<T> |
Task<T> |
System.Threading.Tasks.Task<T> |
DateTime |
System.DateTime |
TimeSpan |
System.TimeSpan |
Guid |
System.Guid |
Special Considerations
Optional Properties
interface Config {
required: string;
optional?: number;
}
optional becomes nullable: double?
Readonly Properties
interface Point {
readonly x: number;
readonly y: number;
}
Generated with only getter: public double x { get; }
Async/Await
async function getData(): Promise<string> {
return "data";
}
Becomes:
public static async Task<string> getData()
{
return "data";
}
Generator Types
Simple Generators
// TypeScript
function* counter(): Generator<number> {
yield 1;
yield 2;
}
Becomes:
public static IEnumerable<double> counter()
{
yield return 1.0;
yield return 2.0;
}
Bidirectional Generators
// TypeScript
function* acc(): Generator<number, void, number> {
let total = 0;
while (true) {
const v = yield total;
total += v;
}
}
Generates wrapper classes for bidirectional communication:
// Exchange class for passing values
public sealed class acc_exchange { ... }
// Wrapper with JS-style API
public sealed class acc_Generator {
public IteratorResult<double> next(double? value = default) { ... }
public IteratorResult<double> @return(object? value = default) { ... }
}
Async Generators
// TypeScript
async function* stream(): AsyncGenerator<string> {
yield "a";
yield "b";
}
Becomes:
public static IAsyncEnumerable<string> stream()
{
// async iterator implementation
}
See also: Generators Guide for complete documentation.