# Reinforced.Typings: generating TypeScript interfaces from your C# DTOs at build time

> Reinforced.Typings is a .NET library and MSBuild integration that turns C# classes, interfaces and enums into TypeScript declarations, so a TypeScript frontend and a .NET backend can share one definition of a DTO instead of maintaining the shape twice. It supports type substitutions, modules, custom code generators, fluent configuration, multi-file export and JSDoc, and it is MIT licensed with a NuGet package of the same name.

**reinforced/Reinforced.Typings** — Converts C# classes to TypeScript interfaces (and many more) within project build. 0-dependency, minimal, gluten-free

- Repository: https://github.com/reinforced/Reinforced.Typings
- Stars: 550 · Forks: 86
- Language: C#
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/reinforced-reinforced-typings

## The problem Reinforced.Typings is built around

The target audience is stated in the first line of the project description. You develop frontend applications with TypeScript and a .NET backend, and you need Reinforced.Typings. It converts .NET classes into TypeScript code, and the framing is that you no longer have to write and maintain boring TypeScript glue code for your DTOs, ViewModels and APIs.

That is a specific and recognisable pain. In a .NET and TypeScript shop, the backend has a C# class and the frontend needs a matching TypeScript interface. Nobody wants to write that interface, and everybody forgets to update it. The result is a frontend that compiles against a type that no longer matches the payload the server sends.

The answer the project gives is not a script you run by hand. Reinforced.Typings integrates with the MSBuild process and regenerates the glue code every time you rebuild the .NET backend, so the stated benefit is that you get immediately notified when the backend's API or DTOs changed and the frontend is broken. The build becomes the synchronisation point, and a mismatch surfaces at compile time on the server rather than as a runtime surprise in the browser.

The feature list from the wiki gives the shape of the tool: type substitutions, modules, code generators, fluent configuration, multi-file export and JSDoc. The package is available on NuGet, and the source tree is a set of separate projects. There is a `Reinforced.Typings/` core library, a `Reinforced.Typings.Cli/` command line front end, a `Reinforced.Typings.Integrate/` project for the build integration, two solution files, one named for .NET Core, a `Reinforced.Typings.Tests/` project, a `Samples/` directory with a Simple and a Difficult sample, and a `cake/` directory for Cake build script integration. The `Reinforced.Typings.settings.xml` configuration file in the project root is where the behaviour is set, and a `Reinforced_pub.snk` strong name key sits beside it.

The project is MIT licensed, and the last push was on 2026-04-19.

## Attributes drive the export, and the class kind decides the TypeScript kind

The mechanism is attribute driven. A class is marked with TsInterface, TsClass or TsEnum, and the tool emits the matching TypeScript construct.

The example in the project description is a small order model with a user type alongside it:

```csharp
namespace MyApp
{
    using Reinforced.Typings.Attributes;

    [TsInterface]
    public class Order
    {
        public string ItemName { get; set; }
        public int Quantity { get; set; }
        public double Subtotal { get; set; }
        public bool IsPaid { get; set; }
        public string ClientName { get; set; }
        public string Address { get; set; }
    }
```

and the TypeScript side that comes out of it is:

```ts
module MyApp {
	export interface IOrder
	{
		ItemName: string;
		Quantity: number;
		Subtotal: number;
		IsPaid: boolean;
		ClientName: string;
		Address: string;
	}
```

The mapping rules are readable from the example. C# string becomes TypeScript string, int and double become number, and bool becomes boolean. A TsClass becomes a TypeScript class with public fields, and a TsEnum becomes a TypeScript enum whose members are numbered from zero, so a C# enum of One and Two comes out as One = 0 and Two = 1.

The `I` prefix on the generated interface is not something you write, it is what the tool applies to a class marked as an interface, which is why the C# class called Order produces IOrder. That is a naming convention you either accept or override through the configuration.

The output is wrapped in a TypeScript module named after the C# namespace, so the result is scoped rather than global, and the nested type reference inside the module is written fully qualified, as MyApp.UserType. Scope by namespace rather than by file is what makes multi-file export workable, since two files in the same namespace can reference each other's types without imports in the naive case.

## Collection types, generics and the mapping that is not obvious

The more interesting example is the view model, because that is where the type mapping stops being one to one.

A C# class with a list of orders, a dictionary keyed by int, a string array and an enumerable of objects becomes:

```ts
module MyApp {
	export interface IPage
	{
		Orders: MyApp.IOrder[];
		Cache: { [key:number]: MyApp.IOrder };
		Tags: string[];
		Things: any[];
	}
}	
```

Three of those four mappings are the obvious ones. List of Order becomes an array of the generated interface, string array becomes string array, and the dictionary becomes an index signature with a numeric key.

The fourth is the one to notice. IEnumerable of object becomes any[]. The tool gives up on the element type and produces a loose array. That is a sensible default, since an unenumerable object has no single TypeScript type to map to, but it means the generated type is weaker than the C# one, and a frontend that wants type safety inside that array has to narrow it by hand.

This is where type substitutions earn their place in the feature list. When the default mapping is not what you want, you can substitute it, and the tool also offers modules, custom code generators, fluent configuration, multi-file export and JSDoc for the cases that the plain attribute does not cover.

The other structural question is what happens to a type you do not want exported at all. The inheritance example shows a class that implements an interface which is not marked for export, and the generated TypeScript keeps only the exported members, so the non-exported interface simply does not appear.

## Inheritance is preserved, including the members the base class already provides

The inheritance example is the one that shows the tool is doing more than flattening properties into an object literal.

A C# class that implements a non-exported interface still produces an interface in TypeScript, and a C# class that implements an exported interface produces a TypeScript interface that extends the generated one:

```ts
module MyApp {
	export interface IWithoutInterface
	{
		Boom: string;
	}
	export interface IEntity
	{
		Id: number;
	}
	export interface IUser extends MyApp.IEntity
	{
		Id: number;
		Login: string;
	}
}	
```

Note that IUser extends IEntity and also repeats Id. The inherited member is emitted in the derived interface as well, which is redundant in TypeScript but harmless, and it has a practical effect: if the derived class re-declares the property, as User does with its own Id, you can see the override in the generated output rather than having to infer it.

The other thing the example demonstrates is that the export decision is per type, not per assembly. INonExport is not marked, so it is not generated, but the class that implements it is marked, and the tool generates a usable interface for it without needing the unimplemented base to be present. That matters in practice, because marking every type in a project for export is not something most teams want to do.

There is also a practical escape hatch for the build itself, documented as RtBypassTypeScriptCompilation in the settings file. The stated purpose is that you will not stay powerless when the generated typings fail your TypeScript build in the project, which tells you the authors assume this will happen. When a generated declaration does not compile in the frontend, you can temporarily bypass that step rather than block your whole build cycle. A setting like that is best turned on deliberately, because a permanently bypassed TypeScript compilation step means you have stopped checking the one thing this tool exists to check.

## Fluent configuration, the settings file and the CLI

Attributes cover the common case, but the tool also offers a fluent configuration path for types you do not own, which is the situation that actually blocks teams.

The example configures a type from a framework namespace, SelectListItem from System.Web.Mvc, rather than one of your own classes:

```csharp
namespace MyApp
{
    using Reinforced.Typings.Fluent;
    using System.Web.Mvc;
    
    public class Configuration
    {
        public static void 
            Configure(ConfigurationBuilder builder)
        {
            builder
            	.ExportAsInterface<SelectListItem>()
                .OverrideNamespace("MyApp")
                .WithPublicProperties();
        }
    }
}
```

The three calls each do something specific. ExportAsInterface selects the type and the output kind, OverrideNamespace moves the emitted type into your namespace rather than the framework's, and WithPublicProperties picks which members are included.

That last point is the real reason the fluent API exists. A framework type carries members you do not want in a TypeScript declaration, and being able to say which ones to include without wrapping the type in a hand written DTO is the difference between a usable tool and one you only use on your own models.

The configuration method is registered in the settings file, and the row underneath the example shows the entry that does it:

Reinforced.Typings.settings.xml: &lt;RtConfigurationMethod&gt;MyApp.Configuration.Configure&lt;/RtConfigurationMethod&gt;

The file is XML, the same file that holds RtBypassTypeScriptCompilation, and it is referenced by name elsewhere in the project structure. The `Reinforced.Typings.Cli/` project in the tree means the same generation is available outside MSBuild, which is what you need when the types live in an assembly that is not part of your backend build, and the `cake/` directory points at Cake script integration for build pipelines that are not MSBuild at all.

For output that is not TypeScript, the custom code generator mechanism documented in the wiki is the extension point, and the project description lists generating custom glue code as a supported use.

## Where Reinforced.Typings is the wrong choice

The first limit is the premise. The tool assumes your C# and TypeScript models are the same shape. If your API deliberately returns a flattened DTO while your domain model is rich, or if you use discriminated unions on the frontend that have no C# counterpart, the generated types will be a poor fit and you will end up writing adapters anyway, which is the glue code the tool was meant to remove.

The second limit is the .NET 10 story. The news section states that version 1.6.7 was released with .NET 10 support and that you should not look for 1.6.6, while the tagged releases in the repository are v1.6.2 from 2023-04-02, v1.5.8 from 2020-07-17 and 1.4.2 from 2017-12-07. The last push was on 2026-04-19. So the useful recent work exists on NuGet rather than in the git tags, and if you pin a tag you will pin a version from 2023. For a build-time dependency in a backend pipeline, that is a decision you have to make consciously.

The third is the coupling. Adding this means adding an MSBuild integration, a settings file and a strong name key to your build, plus a NuGet restore step in CI. That is a real cost for a team whose problem is a handful of mismatched interfaces, and the maintenance document for the project makes clear that the maintainer is the main driver of it.

The fourth is the output being read-only. The generated interfaces are in your repository and your frontend imports them, but they are generated artifacts, and hand editing one is a fight you will lose on the next build. A team that prefers to own its type declarations outright, with a reviewable diff per change and no codegen step, will not get much from this.

Compare that with an OpenAPI or JSON Schema first approach, where the contract is written once and code is generated in both directions. The difference in approach is that Reinforced.Typings takes the C# side as the source of truth and derives TypeScript from it, which is simpler to adopt because the C# types already exist, but it also means the contract cannot describe anything the C# type system cannot express, and it cannot generate server code from a shared schema.

## Licence, project layout and what to check before you wire it into the build

Reinforced.Typings is released under the MIT licence, with the licence text in the LICENSE.md file at the repository root. MIT is permissive and has no copyleft obligation, which makes it comfortable to add to a commercial .NET product, and you should read the LICENSE.md file yourself for the exact wording.

The project is MIT licensed and the package is on NuGet under the name Reinforced.Typings, installed through the Package Manager Console:

```sh
PM> Install-Package Reinforced.Typings
```

The repository layout tells you the project has been worked on for a long time and by more than one person. It has two solution files, `Reinforced.Typings.sln` and `Reinforced.Typings.NETCore.sln`, which is the .NET Framework versus .NET Core split, and a `Reinforced.Typings.Dev.sln` for development. There is a `TestFluentAssembly/` project for the fluent configuration path, a `Samples/` directory with a Simple and a Difficult sample solution, and a `stuff/` directory whose name suggests scratch files rather than shipped code. The wiki is the primary documentation, with the README pointing to it for the full feature list, and the `Samples/Difficult/` sample is likely where the interesting edge cases are demonstrated.

The author note at the top of the project description is worth mentioning because it tells you about the maintenance situation. The project is maintained by Pavel with worldwide contributors since 2014, and the README leads with a recruiting note rather than a changelog.

What to check before you wire it into the build is a short list. Install the package and try it on one DTO first, then look at the generated output before you commit to it, because the mapping decisions are all visible there and that is the cheapest place to disagree with them. Then check the settings file for the bypass option and decide deliberately whether you want the generated typings to be able to fail the TypeScript build, because a permanently bypassed compilation step defeats the point of generating them at build time.

If you have types you do not own, work through the fluent configuration example before writing your own generator, and remember the namespace override, because a framework type emitted under its own namespace will not match what your frontend expects. And since the tagged releases are old, check the NuGet version you actually install against the .NET version you build on.

## Conclusion

Reinforced.Typings fits a team with a .NET backend and a TypeScript frontend that keeps hand written type declarations drifting away from the server, and it fits teams that want the build to fail loudly when a DTO changes. It does not fit a codebase with an existing contract generation step, and it does not fit projects where the C# and TypeScript models are deliberately different shapes, because the tool's premise is that they are the same. Before you adopt it, decide whether the extra .NET and MSBuild dependency is worth removing a class of glue code, read the wiki pages for type substitutions and code generators before writing your own, and set the bypass setting deliberately rather than leaving it enabled by default.

## FAQ

### What does Reinforced.Typings do?

It converts .NET classes into TypeScript code, so you do not have to write and maintain TypeScript glue code for your DTOs, ViewModels and APIs. It integrates with the MSBuild build process and regenerates the glue code every time you rebuild the .NET backend, so a change to a backend DTO surfaces as a frontend type mismatch.

### How do I install Reinforced.Typings?

The package is on NuGet under the name Reinforced.Typings and is installed from the Package Manager Console with `PM> Install-Package Reinforced.Typings`. The behaviour is configured through the Reinforced.Typings.settings.xml file in the project root, which is also where settings such as RtBypassTypeScriptCompilation and the configuration method entry live.

### How are C# types mapped to TypeScript?

A class marked with TsInterface becomes a TypeScript interface with an I prefix, TsClass becomes a TypeScript class, and TsEnum becomes a numbered TypeScript enum. Primitives map directly, with string to string, int and double to number and bool to boolean, and collections map to arrays and index signatures, while an IEnumerable of object becomes any[].

### Can Reinforced.Typings export types I do not own?

Yes. The fluent configuration path lets you export a type such as SelectListItem from System.Web.Mvc with ExportAsInterface, override its namespace and choose which members to include with WithPublicProperties, and you register the configuration method through the RtConfigurationMethod entry in the settings file.

### What licence is Reinforced.Typings under and is it still updated?

The project is released under the MIT licence, with the licence text in the LICENSE.md file at the repository root. The tagged releases are v1.6.2 from 2023-04-02, v1.5.8 from 2020-07-17 and 1.4.2 from 2017-12-07, while the project description announces version 1.6.7 with .NET 10 support, and the last push was on 2026-04-19.

## Sources

- [Issues](https://github.com/reinforced/Reinforced.Typings/issues)
- [License: MIT](https://github.com/reinforced/Reinforced.Typings/blob/master/LICENSE)
- [README](https://github.com/reinforced/Reinforced.Typings/blob/master/README.md)
- [reinforced/Reinforced.Typings on GitHub](https://github.com/reinforced/Reinforced.Typings)
- [Releases](https://github.com/reinforced/Reinforced.Typings/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/reinforced-reinforced-typings
