Deepstaging.Roslyn.TypeScript¶
Fluent builders for generating TypeScript source code from C#.
See also: C# Emit | Types | Expressions
What is this?¶
This satellite package generates TypeScript source code using the same fluent, immutable builder patterns as the C# Emit layer. It's a C# library — you write C#, it outputs TypeScript strings.
Where the C# layer produces Roslyn CompilationUnitSyntax, the TypeScript layer produces raw string output — there's no TypeScript AST in .NET. Optional tsc validation and dprint formatting compensate for this at test time.
The package is organized into three layers:
| Layer | Namespace | Purpose |
|---|---|---|
| Emit | Emit |
Fluent builders (TsTypeBuilder, TsMethodBuilder, etc.) and emit pipeline |
| Types | TsTypeRef + Types |
Composable type references, unions, intersections, utility types |
| Expressions | TsExpressionRef + Expressions |
Composable expressions, optional chaining, expression factories |
Installation¶
Quick Start¶
using Deepstaging.Roslyn.TypeScript;
using Deepstaging.Roslyn.TypeScript.Emit;
var result = TsTypeBuilder.Class("UserService")
.Exported()
.Extends("BaseService")
.AddField("users", "Map<string, User>", f => f
.WithAccessibility(TsAccessibility.Private)
.AsReadonly()
.WithInitializer("new Map()"))
.AddMethod("getUser", m => m
.Async()
.AddParameter("id", "string")
.WithReturnType("Promise<User | undefined>")
.WithBody(b => b
.AddReturn("this.users.get(id)")))
.Emit();
if (result.TryValidate(out var valid))
{
string tsCode = valid.Code; // TypeScript source code string
}
This generates:
// <auto-generated/>
export class UserService extends BaseService {
private readonly users: Map<string, User> = new Map();
async getUser(id: string): Promise<User | undefined> {
return this.users.get(id);
}
}
Mapping from C# Emit¶
If you're familiar with the C# Emit layer, here's how concepts map:
| C# Emit | TypeScript Emit | Difference |
|---|---|---|
TypeBuilder |
TsTypeBuilder |
Adds interface, type alias, enum, const enum |
MethodBuilder |
TsMethodBuilder |
Adds async, generators, optional methods |
PropertyBuilder |
TsPropertyBuilder |
Adds ? optional, getter/setter bodies |
FieldBuilder |
TsFieldBuilder |
Adds #private, declare |
ConstructorBuilder |
TsConstructorBuilder |
Adds parameter properties, super() |
BodyBuilder |
TsBodyBuilder |
Adds for...of, for...in, const/let |
TypeRef |
TsTypeRef |
Adds unions, intersections, tuples, keyof, template literals |
ExpressionRef |
TsExpressionRef |
Adds ?., ??, as, satisfies, ===, spread |
OptionalEmit → ValidEmit |
TsOptionalEmit → TsValidEmit |
Same pattern, string output instead of syntax tree |
EmitOptions |
TsEmitOptions |
Adds tsc validation, dprint formatting |
Key Differences¶
- Export vs. Accessibility: TypeScript uses
exportfor module visibility. Use.Exported()instead of.WithAccessibility(Accessibility.Public)at the type level. - No namespaces: TypeScript uses ES modules. Use
.AddImport()for dependencies. - Union/Intersection types: TypeScript has first-class union (
A | B) and intersection (A & B) types — useTsTypeRef.Union(...)andTsTypeRef.Intersection(...). - Optional members: TypeScript has
?syntax — use.AsOptional()on properties, parameters, and methods. - Parameter properties: TypeScript's constructor shorthand (
constructor(public name: string)) — use.AsParameterProperty(). #privatefields: ES private field syntax — use.AsEsPrivate().- String output: No Roslyn syntax tree — you get a
string. Usetscvalidation in tests if you need compile-time guarantees.
Pages¶
- Emit —
TsTypeBuilder, member builders,TsBodyBuilder,TsEmitOptions, emit pipeline - Types —
TsTypeRef, specialized type refs, utility type refs - Expressions —
TsExpressionRef, expression factories - Testing —
TsTestBase, option presets, assertions,VerifyEmitsnapshots - Integration — Real-world usage: analyzer + code fix, MSBuild extraction, CLI tool