SimpleTypeScript 1.1.0

dotnet add package SimpleTypeScript --version 1.1.0
                    
NuGet\Install-Package SimpleTypeScript -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SimpleTypeScript" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SimpleTypeScript" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="SimpleTypeScript" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SimpleTypeScript --version 1.1.0
                    
#r "nuget: SimpleTypeScript, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SimpleTypeScript@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SimpleTypeScript&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=SimpleTypeScript&version=1.1.0
                    
Install as a Cake Tool

SimpleTypeScript 📘

Platform NuGet License: MIT

An emitter for generated TypeScript. Build a module out of declarations, types and values; the library decides how each of them is spelled, once, so every generator writing through it produces the same shape.

var module = new TsModule()
    .Const("LOCALES", TsValue.AsConst(TsValue.Array(["en-US", "sv-SE"])))
    .Const(
        "MESSAGES",
        TsValue.Object(new Dictionary<string, string> { ["greeting"] = "Hej värld" }),
        TsType.Record(TsType.String, TsType.String),
        doc: "Every key, authored in the neutral culture.");

File.WriteAllText(path, module.Render());
// GENERATED — do not edit.

export const LOCALES = ["en-US","sv-SE"] as const;

/** Every key, authored in the neutral culture. */
export const MESSAGES: Record<string, string> = {
  "greeting": "Hej värld",
};

Shapes as well as values:

string[] statuses = ["Queued", "Running"];

var module = new TsModule()
    .TypeAlias("ScanStatus", TsType.Union(statuses.Select(TsType.StringLiteral)))
    .Interface(
        "ScanSummary",
        [
            new TsMember("id", TsType.String) { IsReadOnly = true },
            new TsMember("status", TsType.Of("ScanStatus")),
            new TsMember("finishedAt", TsType.Union([TsType.String, TsType.Null])) { Doc = "Null while running." },
        ],
        doc: "One scan, as the API returns it.");
export type ScanStatus = "Queued" | "Running";

/** One scan, as the API returns it. */
export interface ScanSummary {
  id: string;
  status: ScanStatus;
  /** Null while running. */
  finishedAt: string | null;
}

Generating those declarations from C# types is SimpleTypeScript.TypeGeneration, a companion package. This one has no dependencies and reflects over nothing.

What it is for

Generated code is compiled before anyone reads it, so a rule that is merely probably right produces a file that is broken where nobody is looking. The rules that are easy to get wrong are the ones this owns:

  • String literals are escaped for ECMAScript source, not for JSON. The two differ where it matters: JSON leaves U+2028 and U+2029 raw inside a string, ECMAScript ends a line on them. A lone surrogate is escaped for the same reason — it cannot be encoded as well-formed UTF-8. Everything else printable is written as itself, because a generated vocabulary is read by whoever checks a translation and Ã¥ is no use to them.
  • A comment cannot be ended from inside it. Text carrying a line terminator would put the rest of itself into the module as code, so every terminator re-opens the comment — including the two only ECMAScript treats as one — and a doc comment's */ is neutralised.
  • Numbers are invariant and round-trippable, and NaN and the infinities are refused rather than emitted as something that parses and means another thing.
  • Output is byte-stable. Line endings are \n whatever the platform spells a newline as, and order is the caller's, so regenerating on another machine does not rewrite every line of a file that did not change.

Nothing here reflects: a value is built explicitly, so what is emitted is what the generator said to emit, and no trimming or AOT caveat is carried into a consumer.

The model

Type What it is
TsModule A header comment, then exported declarations. Holds declarations rather than their text, so what a module is stays inspectable until the moment it is written.
TsValue A value literal: String, Number, Boolean, Null, Array, Object. Objects always break across lines; arrays stay on one while everything inside fits.
TsType An annotation: the primitives, Null, Record<,>, T[], readonly T[], a named reference, and a union or intersection of any of them.
TsMember One property signature: a name, a type, readonly, a doc comment.
TsComment Lines for a // header, Doc for the /** … */ an editor surfaces at the use site, Empty for a module that opens with no banner at all.
TypeScriptException The refusal: a binding name that is not one, a number with no literal form, an interface with no members.

Three types here do not close with their own syntax, so TsType carries how tightly each binds and a container brackets anything looser than the position allows — ("a" | "b")[] rather than "a" | "b"[], which is a different type that compiles just as well. A rank rather than a flag, because the three disagree with each other: an array brackets a union and an intersection, an intersection brackets only the union, and B & readonly A[] needs nothing while (readonly A[])[] does.

readonly T[] is there because readonly on a member is only half the promise — readonly lines: Line[] refuses order.lines = [] and permits order.lines.push(x). Saying it properly needs the modifier on both.

An intersection is there for the open set. A generator describing a producer's vocabulary that may grow wants the known members to stay the editor's suggestions while a value nobody has seen is still assignable, and Known | string does not do that — TypeScript reduces it to string. Known | (string & Record<never, never>) does.

Still deliberately not the whole type system. Conditionals, mapped types and generics beyond Record and the two arrays have no shape here, because every level of a grammar that is modelled has to be kept correct. A composed type written as a name is refused by TsType.Of rather than half-supported: a type is something to build, not something to spell.

Installing

dotnet add package SimpleTypeScript

net8.0 and net10.0.

License

MIT. See LICENSE.txt.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on SimpleTypeScript:

Package Downloads
SimpleTypeScript.TypeGeneration

Generate TypeScript declarations from C# types: walk a set of roots, follow what their members reach, and write an interface per type and a string-literal union per enum — reading the shape System.Text.Json actually serializes, not only the shape the C# declares.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 106 8/20/2026
1.0.0 237 8/3/2026
0.5.0 122 8/3/2026