Skip to content

Instantly share code, notes, and snippets.

@dfoverdx
Created July 7, 2021 18:29
Show Gist options
  • Select an option

  • Save dfoverdx/147b76a695d22e26e36097c987897d0b to your computer and use it in GitHub Desktop.

Select an option

Save dfoverdx/147b76a695d22e26e36097c987897d0b to your computer and use it in GitHub Desktop.

Revisions

  1. dfoverdx created this gist Jul 7, 2021.
    55 changes: 55 additions & 0 deletions CompositeEnum.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,55 @@
    /**
    * 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 [email protected] 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;