Home > Enterprise >  TypeScript select props with generics
TypeScript select props with generics

Time:09-26

Is there a cleaner way to implement this behavior using generics?

The point of this Type is to allow user to pass in either data, color, hasColor or data, info, hasInfo. Not any other combination.

type Props = {
  data: string;
} & (
  | {
      info?: string;
      hasInfo?: boolean;
      color?: never;
      hasColor?: never;
    }
  | {
      info?: never;
      hasInfo?: never;
      color?: string;
      hasColor?: boolean;
    }
);


function foo(props: Props) {
  console.log("bar");
}

foo({ data: "hello", info: "hello", hasInfo: true }); <----- TypeScript is happy
foo({ data: "hello", info: "hello", hasColor: true }); <----- TypeScript gives Error

I tried this, but it looks like I'm messing up the logic of the ternary somehow:

type Info = { info: string; hasInfo: boolean };
type Color = { color: string; hasColor: boolean };
type Data = { data: string };

function foo<T>(
  props: keyof T extends keyof Info ? Data & Info : Data & Color
) {
  console.log("bar");
}

foo({ data: "hello", color: "hello", hasColor: true }); <----TypeScript gives Error

The first way works but looks so ugly.

CodePudding user response:

With your current definiton, TypeScript is not able to infer the type parameter T, so it is inferred as unknown when calling the function. Therefore, keyof T is just never and the conditional always resolves to the false branch.

To fix this, we can intersect the result of the conditional with T.

function foo<T>(
  props: keyof Info extends keyof T 
    ? Data & Info & T 
    : Data & Color & T
) {
  console.log("bar");
}

Also note that we have to switch keyof T extends keyof Info to keyof Info extends keyof T. keyof T will be the larger union and therefore can't extend keyof Info.


Playground

  • Related