Skip to content

Instantly share code, notes, and snippets.

@liammcinroy
Created May 15, 2025 16:29
Show Gist options
  • Select an option

  • Save liammcinroy/9683be868c6ec8d6094fe322d5f82363 to your computer and use it in GitHub Desktop.

Select an option

Save liammcinroy/9683be868c6ec8d6094fe322d5f82363 to your computer and use it in GitHub Desktop.
Ways to extend typescript string enums with typing (even if compile-time only)
/*
* Extending string enums, especially if they aren't provided at runtime.
* Playground: https://www.typescriptlang.org/play/?#code/PQKgUABCEKIB4BcCmA7AJgSxQcwgZwQCctdUBXAWzwBoIk8AHJAYwwEMAbDgTwgwDMICABZJebQqgDkCCA0IB7AG4Y0SNBDazCZFAgwUkAOkghgYMKAgAVUUOEkIASSkaOGANZIhCuolQaGLIA7kHCUObkFBAAgigKIkiE1txMEADekLHxiYQQALwQAERsOaKERWAAvhZWToKJvMFJ0rLyyqrqmtq6+oYQABR4vvH48khsaHgAlBGWwHR47noAtJh4bABGHEgrKEiIK8veAAIIqfTMxAwIK-TLCMDxe2yGjGzMSGBqzBwS3ihXvQGB9vDEMlkohB4moAPrnNKZCDIiDABb3LC3dZbHZ7A63Y4QM4XPBXDA3O5LTFPBQrPDCSYKYJZZHgwolSrImo1NGLB5rDAbba7faHQnEpik663DF6GkvN4gz7fFh-STQoHvT4QABCEORUJhSHhF31KN5sqxgpxIvxRywpwRl2llIe8vpjOZKN1BWKm05EG5FidEAAcgo1Ck0oUYkYjSa0gAfXVxiPGkPJuIJcpRpAAblqCwAIgp6CgZBBggpCB4wMwFCgCGG07mAGJsDAcPAALmbkYu4ZQoc1Su8hSRsV7HOoWR1U-9M+RRmXWdyueqFmDpvD-aYAHlBPkssmnQpBLH407j0ILmeU5eLtfT+eyklcwWQ2w8LD3MhCJwAB5rAAPl9aw-GQdA8HwIhHAAfggAADAASdJrCqRCIF7fYlCSD9TScCgGA4HckFzX0JwAbXIrA+zIi4D00aCvG4O9cwAXV7L8fyCJJANzSiWLYi52OAjd5ggAB1asPDwABCCTLQFIVcVFAkHWhWlJB+NUvnrRtZFI3Ne0I4ijNNccshiKc2CKRddXnOysmXIxVxzR8alqaB6nsMRNHVdoVDUDQhl8RhJEmaD-k0LgmXUWYzDAKEYkHQFFVBM02WKWyNyhHVUpHDKJz1dl-XEkNzKYQdh3S7UYwK2rvGTfKGzS4EMszV9kguAsJJLMsKyrGs6wbJtKqQarCs+dtOx7Ojc0mxqKOclcGvaz57JclqUDarUkE2lcuvXTyKpbBj+EW9axyfW9zzWvabqYO9tt20dHqQO83LfHqwBDUySLOqqGxqq7luRajTVo8aD0uvamIgITBA43sBMRmwRPE3lpJreTFKpVZsWFPExQ055tNVf4RoM+aB2BqakBMoiAd3Ca6aWyyl1W1r6YOowXp5lbXKOjyLCAA
*/
/* The thing I'd like to extend it with */
enum AnotherType {
Another = "another"
}
/* If they weren't provided at runtime (so no spreads) */
// eslint-disable-next-line @typescript-eslint/no-namespace
declare namespace A {
enum node_type {
// eslint-disable-next-line @typescript-eslint/no-shadow
A = "a"
}
}
// eslint-disable-next-line @typescript-eslint/no-namespace
declare namespace B {
enum node_type {
// eslint-disable-next-line @typescript-eslint/no-shadow
B = "b"
}
}
type NodeType = A.node_type | B.node_type | AnotherType;
// Doesn't work
const NodeTypeFails: NodeTypeNonNamespace = {
A: "a",
B: "b",
...AnotherType
}
type NodeTypeOf =
| typeof A.node_type
| typeof B.node_type
| typeof AnotherType;
type as_literal<T> = T extends string ? `${T}` : never;
type ImplNodeType = {
[Type in NodeTypeOf as keyof Type]: as_literal<Type[keyof Type]>
}
// Works!
// eslint-disable-next-line no-redeclare
const NodeType: ImplNodeType = {
A: "a",
B: "b",
...AnotherType
}
/* If they are provided (so spreads are allowed) */
enum ANonnamespace {
A = "a"
}
enum BNonnamespace {
B = "b"
}
type NodeTypeNonNamespace = ANonnamespace | BNonnamespace | AnotherType;
// Doesn't work
const NodeTypeNonNamespaceFails: NodeTypeNonNamespace = {
...ANonnamespace,
...BNonnamespace,
...AnotherType
}
type NodeTypeOfNonNamespace =
| typeof ANonnamespace
| typeof BNonnamespace
| typeof AnotherType;
type ImplNodeTypeNonNamespace = {
[Type in NodeTypeOfNonNamespace as keyof Type]: Type[keyof Type]
}
// Works!
// eslint-disable-next-line no-redeclare
const NodeTypeNonNamespace: ImplNodeTypeNonNamespace = {
...ANonnamespace,
...BNonnamespace,
...AnotherType
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment