Skip to content

TypeGen — Type mapping

The emitters translate C# types to target-language equivalents. Defaults:

C#TypeScriptOpenAPIPython (Pydantic)
int, long, shortnumberinteger (int32/int64)int
float, doublenumbernumber (float/double)float
decimalstring (precision!)number (double)str (precision!)
stringstringstringstr
boolbooleanbooleanbool
System.Guidstringstring (uuid)UUID
System.DateTimestringstring (date-time)datetime
System.DateOnlystringstring (date)date
T? (nullable)optional ?nullable: true + not in requiredT | None = None
List<T>, T[]T[]type: array, items: ...list[T]
Dictionary<K, V>Record<K, V>type: object, additionalProperties: Vdict[K, V]
User DTOTypeName$ref: '#/components/schemas/TypeName'TypeName (imported)
enumexport enum (numeric values)type: string, enum: [...]IntEnum
enum with [JsonConverter(typeof(JsonStringEnumConverter))]export type X = "A" | "B"; (default — TsEnumStyle.Union)type: string, enum: [...](str, Enum)

Override any single property with [TsType("...")] or [OpenApiProperty(Format = "...")].

When the type expression names an external symbol (your own DTO, a third-party type, a branded type alias) the generator can emit the matching import line for you. Pass ImportFrom as a named argument:

[TsType("AutomationRulePayload", ImportFrom = "./types/automation-rule-payload")]
public JsonObject? Element { get; set; }

→ at the top of the generated file:

import { AutomationRulePayload } from './types/automation-rule-payload';

Multiple PascalCase identifiers in the expression all get pulled from the same path:

[TsType("Map<Foo, Bar>", ImportFrom = "./types/api")]
public object Item { get; set; }

import { Bar, Foo, Map } from './types/api';

Primitives (string, number, boolean), literal unions ('a' | 'b') and similar non-importable tokens are left alone. Two properties pointing at the same path get merged into one import line; different paths get separate lines.

The same is available via the fluent configurator — .TsType("Foo", "./bar"):

b.ForType<Article>()
.Property(a => a.Element).TsType("AutomationRulePayload", "./types/automation-rule-payload");

When ImportFrom is null / omitted (or the type expression is a primitive like "string"), no import is emitted — the override is treated as opaque.

[UseType<T>] — cross-target generic override (C# 11+)

Section titled “[UseType<T>] — cross-target generic override (C# 11+)”

Refactor-safe, cross-target replacement for the string form. One attribute — every emitter handles it in its own idiom:

targetemitted
TypeScriptprop: T; + import { T } from './T'; (path auto-computed)
OpenAPI$ref: '#/components/schemas/T'
Pythonprop: T + from t import T
[GenerateTypes(Targets = TypeTarget.TypeScript | TypeTarget.OpenApi, OutputDir = ".")]
public class Rule
{
[UseType<AutomationRulePayload>]
public JsonObject? Element { get; set; }
}
[GenerateTypes(Targets = TypeTarget.TypeScript | TypeTarget.OpenApi, OutputDir = ".")]
public class AutomationRulePayload { public string Body { get; set; } = ""; }

→ TypeScript:

import { AutomationRulePayload } from './AutomationRulePayload';
export interface Rule {
element?: AutomationRulePayload;
}

→ OpenAPI:

Rule:
type: object
properties:
Element:
$ref: '#/components/schemas/AutomationRulePayload'

Cross-directory TS imports — different OutputDir values, one per type:

[GenerateTypes(OutputDir = "client/src/rules")] public class Rule {
[UseType<Payload>] public object? El { get; set; }
}
[GenerateTypes(OutputDir = "client/src/types")] public class Payload { /* … */ }

import { Payload } from '../types/Payload'; — the .. up-traversal is computed automatically from the two OutputDirs’ common ancestor.

External targets (BCL types, NuGet packages, hand-written .d.ts) — the symbol lives outside the current compilation so TS auto-path doesn’t apply. Pair with explicit ImportFrom (TS-only — OpenAPI / Python reference T by name regardless):

[UseType<ExternalLib.Widget>(ImportFrom = "@acme/widgets")]
public object? W { get; set; }

[TsName] on the target is honored — if Payload carries [TsName("PayloadDto")], both the TS type expression and the import use PayloadDto.

Enums work too: [UseType<Priority>] against [GenerateTypes] public enum Priority { … }.

Fluent equivalent:

b.ForType<Rule>()
.Property(r => r.Element).UseType<AutomationRulePayload>();

When to use [TsType("…")] instead — for TS-only opaque expressions that have no semantic in OpenAPI or Python: literal unions ("'pending' | 'done'"), branded types ("string & { __brand: 'email' }"), complex generics hand-written in TypeScript. Those stay on the string form.

Requires C# 11+ in the consuming project (generic attributes).