C# → TypeScript converter
Paste your C# DTOs — classes, records, structs, interfaces and enums — and get the
TypeScript interfaces that describe the JSON System.Text.Json actually writes
for them: camelCase names, T | null where the C# is nullable, arrays for every
collection, Record<string, T> for dictionaries, strings for dates and GUIDs,
and a note for every mapping. Nothing is guessed: a long gets a precision
warning, a type you did not paste is flagged, a method is listed as skipped.
Your code is parsed on the server and forgotten. Nothing is stored, nothing is logged and nothing is compiled or executed: the converter reads the syntax tree of the text you paste, maps the types by name and discards the input.
Needs your judgment
Why it looks like this
Options go on the first line of the input as a comment, for example
// ts: shape=type naming=asIs nullable=optional enum=string. The defaults are
what ASP.NET Core sends with JsonSerializerDefaults.Web: interface,
camelCase names, nullable members as T | null, enums as numbers,
export on every type, dates as string. nullable=optional
writes name?: T instead, nullable=both writes
name?: T | null, enum=string writes a union of the member names,
fields=include adds public fields for code that sets IncludeFields, and
dates=Date emits Date with a warning, because the JSON still carries
strings.
Free API — use it from your own tools
The same engine powers an open API (this page uses it too). No signup, no key — just a rate limit of 60 requests per minute per IP. Handy for a build step that keeps the front end's types in line with the API's DTOs.
curl
curl -X POST https://www.coder000.com/api/v1/csharp-to-typescript \
-H "Content-Type: application/json" \
-d '{"input": "public class Patient { public int Id { get; set; } public string? Email { get; set; } }"}'
C#
using var http = new HttpClient();
var source = await File.ReadAllTextAsync("Contracts/Patient.cs");
var response = await http.PostAsJsonAsync(
"https://www.coder000.com/api/v1/csharp-to-typescript",
new { input = source });
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
await File.WriteAllTextAsync("ClientApp/src/patient.ts", result.GetProperty("output").GetString());
Uniform response shape: { "output": "…", "notes": [...], "warnings": [...] } —
output is the TypeScript source, and each note carries a change,
an explanation, and where there is one a link to the post behind
the decision. Send any number of type declarations per request, with or without
namespaces and usings, up to 100,000 characters.
How it works, and what it cannot do
No AI and no compiler run. Roslyn, the C# parser that Visual Studio and the
dotnet CLI use, turns the text into a syntax tree; a fixed mapping table
turns each declaration into TypeScript. The output was checked with the TypeScript
compiler, and the interfaces were checked against the JSON System.Text.Json
really writes for the same classes, before this page went live. The rules:
- Types by name, not by meaning. Because nothing is compiled, a type is
mapped by how it is written:
int,decimalanddoublebecomenumber;longbecomesnumberwith a warning, because JavaScript numbers lose precision above 253;string,char,Guid,Uri,DateTime,DateTimeOffset,DateOnly,TimeOnlyandTimeSpanbecomestring, which is what the serializer writes;byte[]is a base64string;object,dynamicandJsonElementbecomeunknown. - Collections and dictionaries. Arrays,
List<T>,IEnumerable<T>,HashSet<T>and the other collection interfaces all becomeT[], because JSON has one array shape.Dictionary<K, V>becomesRecord<string, V>, because JSON object keys are always strings, whateverKwas. - Nullable and required.
int?andstring?becomeT | null, because the serializer writesnullrather than dropping the property. A member with[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]becomes optional (name?:).requiredchanges nothing on the wire: it is a rule for reading, and a null required value is still left out under that condition, so the note says so.{ get; }members arereadonly. - Attributes the serializer honours, honoured here.
[JsonPropertyName](and Newtonsoft's[JsonProperty]) renames,[JsonIgnore]removes,[JsonInclude]adds a non-public member,[JsonExtensionData]becomes an index signature,[JsonStringEnumConverter]turns an enum into a union of strings,[JsonPolymorphic]and[JsonDerivedType]add the type discriminator to the base interface and turn a property typed as the base into a union of the derived types,[Obsolete]becomes@deprecated, and a/// <summary>becomes a JSDoc comment. - Enums are numbers unless you say otherwise. That is the
System.Text.Jsondefault, and the most common surprise in a front end. With a string converter on the enum or on the property, orenum=string, the output is"Pending" | "Paid" | …instead. A[Flags]enum is typedEnum | number, because a combined value such as 5 has no name. - Inheritance and references.
class Dog : Animalbecomesinterface Dog extends AnimalwhenAnimalis in the input. A base type or a property type you did not paste gets an empty placeholder interface at the end of the output and a warning, so the file still type-checks; paste every type the DTOs mention to get the real shape. - What it skips. Methods, constructors, operators, indexers, events,
static and constant members, non-public members: none of them are JSON, so none of
them appear, and each type's note says how many were left out. Public fields are left
out too, because the serializer ignores fields unless
IncludeFieldsis on; thefields=includedirective turns them on. Value tuples becomeunknownwith a warning, becauseSystem.Text.Jsonwrites{}for them, and so donint,Task<T>and other types the serializer refuses. Options you set withJsonSerializerOptionsinProgram.csare invisible to the converter; tell it with the// ts:line instead. - Not a compiler. Code that would not compile still converts as far as it parses; the parser's complaints come back as warnings with line numbers. Generic types keep their type parameters. Nested types are flattened to their own name. Input nested deeper than 256 brackets is refused before parsing, because the parser itself cannot take it. TypeScript → C# is not offered yet.
Learn the why, not just the what
The mapping table, the five traps and the proof behind every rule on this page.