SimpleTypeScript 1.1.0
dotnet add package SimpleTypeScript --version 1.1.0
NuGet\Install-Package SimpleTypeScript -Version 1.1.0
<PackageReference Include="SimpleTypeScript" Version="1.1.0" />
<PackageVersion Include="SimpleTypeScript" Version="1.1.0" />
<PackageReference Include="SimpleTypeScript" />
paket add SimpleTypeScript --version 1.1.0
#r "nuget: SimpleTypeScript, 1.1.0"
#:package SimpleTypeScript@1.1.0
#addin nuget:?package=SimpleTypeScript&version=1.1.0
#tool nuget:?package=SimpleTypeScript&version=1.1.0
SimpleTypeScript 📘
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
NaNand the infinities are refused rather than emitted as something that parses and means another thing. - Output is byte-stable. Line endings are
\nwhatever 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 | Versions 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. |
-
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.