Skip to content

Instantly share code, notes, and snippets.

@Nyrox
Created February 24, 2026 13:46
Show Gist options
  • Select an option

  • Save Nyrox/4ea32e8b3de0aaa5e214511869ab2289 to your computer and use it in GitHub Desktop.

Select an option

Save Nyrox/4ea32e8b3de0aaa5e214511869ab2289 to your computer and use it in GitHub Desktop.
type Serializer<From, To> = {
serialize: (v: From) => To;
deserialize: (v: To) => From;
};
const DateSerializer: Serializer<Date, number> = {
serialize: (d) => d.getTime(),
deserialize: (n) => new Date(n),
};
type DeepWriteable<T> = T extends object ? { -readonly [P in keyof T]: DeepWriteable<T[P]> } : T;
type Fail<Message extends string> = {
__message__: Message;
};
type UnFail<T> =
T extends Fail<string> ? never : T extends object ? { [K in keyof T]: UnFail<T[K]> } : T;
type ToTypeString<T, Fallback extends string> = T extends string | number | boolean
? T
: T extends Date
? 'Date'
: Fallback;
type NoExtraKeys<Candidate, Target> =
Exclude<keyof Candidate, keyof Target> extends never
? true
: Fail<`Serializer contains keys not present in target type.`> & {
extraKeys: ToTypeString<Exclude<keyof Candidate, keyof Target>, 'unknown'>;
};
type Assert<Cond extends true | Fail<string>, T> = Cond extends true ? T : Cond;
type ApplySerializer<S extends Record<string, unknown>, T extends Record<string, unknown>> = Assert<
NoExtraKeys<S, T>,
{
[K in keyof T]: S extends { [SK in K]: object }
? S[K] extends Serializer<T[K], infer To>
? To
: S[K] extends Serializer<infer From, infer To>
? Fail<`Serializers contains serializer '${ToTypeString<K, 'K'>}' :: '${ToTypeString<From, 'From'>}' -> '${ToTypeString<To, 'To'>}', but corresponding type in target type is '${ToTypeString<T[K], 'incompatible'>}'`>
: S[K] extends Record<string, object>
? T[K] extends Record<string, unknown>
? ApplySerializer<S[K], T[K]>
: Fail<`Serializers contains nested serializer '${ToTypeString<K, 'K'>}', but corresponding target type '${ToTypeString<T[K], 'unknown'>}' is not an object.`>
: Fail<`Serializers contains key '${ToTypeString<K, 'K'>}' which is not a valid serializer or record.`>
: T[K];
}
>;
/**
* ^
* |
* |
* | DANGER
*
*
*
* THE WALL WHICH PROTECTS US
*
*/
const DocumentSerializer = {
createdAt: DateSerializer,
nested: {
createdAt: {} as Serializer<string, number>,
},
} as const;
type SerializedDocument = ApplySerializer<
typeof DocumentSerializer,
Document
>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment