Typescript generation with NodaTime

I am looking into using NodaTime types in my DTOs to express the difference between a date and datetime (LocalDate and Instant).
I have added the NodaTime.Serialization.ServiceStackText library and JSON serialization works well now for both those types.
However, now when I regenerate the typescript client DTOs, I get errors as it emits the LocalDate type for each property (which ts knows nothing about).

Taking a look at how native DateTime is handled, the code-gen does neatly handle it for each client language (Typescript falls back to strings, c# is kept as DateTime’s).

How can I configure SS to do the same for the NodaTime types used across all DTOs?

TypeScript code generation is unique in that it doesn’t do any serialization/deserialization into the languages native types as JSON is parsed into JS as-is, i.e the TypeScript Definitions are just describing the serialized JSON that is emitted which needs to serialize into JSON primitive Types which as it doesn’t support Dates, DateTime’s need to be sent as strings.

You’re not going to be able to get native handing across all DTOs into their native types, each language is going to need explicit configuration on both the server about what destination Type it should emit each custom type as (e.g. in TypeScriptGenerator.TypeAliases) then the serializer for each language is going to need to be configured with how to handle that Type. But as TypeScript doesn’t use a serializer nor has any dependencies it needs to be sent as a JSON type, e.g. a string. All other languages allow their JSON serializers to be customized albeit all need to be done differently given they all have different implementations.

The easiest way would be to register a custom serializer for the Type e.g. in TypeScriptGenerator.TypeAliases) you want to serialize and serialize it into an ISO8601 or WCF JSON Date format:

\/Date({msSinceEpoch}+{TZ Offset})\/

But even then most native Date types doesn’t support configurable TimeZone’s which is why Date’s are sent as UTC which basically defeats the purpose of using Noda time. If you go this approach you’ll still need to register TypeAliases in each of the languages mapping it to the String type in each language.

If you want to preserve the timezone you could convert it into a custom typed DTO that preserves the Date and TImeZone in different properties where I’d add an extension method that converts the NodaTime date into your DTO, e.g:

new MyDto {
    MyDate = nodaTime.ToDto()

The benefit of this Custom DTO approach is that you wont need to configure the custom serializers in each language as it’s just being returned as a custom POCO DTO. If you go this option I’d recommend also register Automapping converters so the AutoMapping APIs will also transparently work.

It’s also worth mentioning this dedicated package which is built around serializing Nodatime in ServiceStack.Text:

But as I don’t see any references to the native type generators, you’ll still need to configure them on both client/server for each language.

Thanks Demis - TypeScriptGenerator.TypeAliases is what I was looking for.
Note I am only looking at using 2 nodatime types within DTO contracts - LocalDate and Instant. LocalDate being a timezone-less date-only construct which neatly serializes as ‘yyyy-mm-dd’ and Instant being a UTC only timestamp that serializes as the full iso8601 UTC date and time format.
Agreed there is more effort to have it work in all clients, but I only need c# and ts for now.

Interestingly, I’ve only had to add
TypeScriptGenerator.TypeAliases.Add(nameof(LocalDate), "string"); to fix LocalDate’s.
For some reason, NodaTime.Instant typed DTO props are already generating as a string in ts. Any ideas why this is the case?

As Instant is a struct it follows ServiceStack.Text’s Struct behavior where it’s serialized into a single scalar value.