Search code examples
reactjstypescriptgenericstypeof

How to extract generic type in interface or typeof type in Typescript?


I'm trying to use generics and interface to make React component.

interface MyAtomComponent {
  <P>(props: P): React.ReactElement;
  WithSomeOption: <P>(props: P): React.ReactElement;
}

// component definition
const MyAtomComponent: MyAtomComponent = <P,>(props: P) => { ... };
MyAtomComponent.WithSomeOption = <P,>(props: P) => { ... };

// in use
<MyAtomComponent<{/* something */}> />
<MyAtomComponent.WithSomeOption<{/* something */}> />

It looks good, but problem occurs in this case:

type MoleculeProps = {/* molecule prop */} & MyAtomComponent['WithSomeOption'] // Generic of WithSomeOption is gone

const Molecule = ({...}: MoleculeProps<number>) => { ... }; // I cannot put `number` because MoleculeProps is not generic

I tried like this:

type AtomPropsWithGeneric = MyAtomComponent['WithSomeOption'];
// AtomPropsWithGeneric is now <P>(props: P): React.ReactElement

const Test: AtomPropsWithGeneric<{}> = ... // AtomPropsWithGeneric is not generic.

and also this failed too:

type AtomPropsWithGeneric<P> = MyAtomComponent['WithSomeOption']<P>; // ';' expected

I think [] in type cannot extract generic; typeof also print same error(';' expected).

Is any way to extract generic type in interface?


stack overflow suggested this answer but I think this case is little bit different.


Solution

  • You can use instantiation expressions (#47607):

    type WithSomeOption<P> = typeof MyAtomComponent.WithSomeOption<P>;
    

    It's very important to use dotted access instead of bracket notation, as the latter will not parse:

    // not valid TS
    type WithSomeOption<P> = typeof MyAtomComponent["WithSomeOption"]<P>;
    

    We also need typeof since dotted access on interfaces is disallowed (or else it could be mistaken for a namespace):

    // will error; "Cannot access 'MyAtomComponent.WithSomeOption' because 'MyAtomComponent' is a type, but not a namespace."
    type WithSomeOption<P> = MyAtomComponent.WithSomeOption<P>;
    

    Playground