TypeScript composite `as const`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Creates a readonly object which combines the given `as const` constants. If any of the objects overlap in keys or | |
* values, it evaluates to `never`. Unfortunately this does not work with enums because there is no way to determine if | |
* the *value* `Enum1.foo` is different than that `Enum2.bar`. Typescript treats enum values as distinct types. | |
* | |
* I made this type to prevent flux action types from overlapping. | |
* | |
* Requires Typescript@4.2 or later. | |
*/ | |
type CompositeAsConst< | |
T extends readonly Record<string, string | number>[], | |
C extends Record<string, string | number> = {} | |
> = | |
T['length'] extends 0 | |
? Readonly<{ [K in keyof C]: C[K] }> | |
: T extends [infer Head, ...infer Tail] | |
? (keyof Head & keyof C) | (Head[keyof Head] & C[keyof C] & (number | string)) extends never | |
? Tail extends readonly Record<string, string | number>[] | |
? ( | |
& { [K in keyof C]: C[K] } | |
& { [K in keyof Head]: Head[K] } | |
) extends infer Next | |
? Next extends Record<string, string | number> | |
? CompositeAsConst<Tail, Next> | |
: never | |
: never | |
: never | |
: never | |
: never; | |
/** Examples */ | |
const Foo = { | |
foo: 'foo' | |
} as const; | |
const Bar = { | |
bar: 'bar' | |
} as const; | |
const Baz = { | |
baz: 'baz', | |
foo: 'foo' | |
} as const; | |
const Baz2 = { | |
baz: 'foo' | |
} as const; | |
function compositeAsConst<T extends readonly Record<string, string | number>[]>(...constants: T): CompositeAsConst<T> { | |
return Object.assign({}, ...constants); | |
} | |
const FooBar = compositeAsConst(Foo, Bar); // => { readonly foo: 'foo'; readonly bar: 'bar'; }; | |
const FooBaz = compositeAsConst(Foo, Baz); // => never; | |
const FooBaz2 = compositeAsConst(Foo, Baz2); // => never; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment