'Constrain tuple values where 2nd value is constrained by the 1st value

I can do the following easily with a function, but to create a simpler API for the consumer I want to do it with a tuple.

Say I have 2 simple React components that take different props:

const Text = ({ value }: { value: string }) => <span>{value}</span>;
const PrefixedText = ({ prefix, value }: { prefix: string; value?: string }) => (
  <span>{value ? `${prefix}${value}` : ''}</span>
);

And an object that references them:

const renderPropMap = {
  text: Text,
  prefixedText: PrefixedText
};

type RenderPropMap = typeof renderPropMap;

Now I want to create a type that is a tuple where the 1st element is a key of renderPropMap ('text' or 'prefixedText') and the 2nd element is the props of the component at the key of the given 1st element. AND I would like the IDE to auto-suggest the 2nd element once the first has been entered.

I can easily create a function that does this with its arguments and returns the desired tuple:

type RenderProps<R extends keyof RenderPropMap> = [R, Parameters<RenderPropMap[R]>[0]];
function setRender<R extends keyof RenderPropMap>(...args: RenderProps<R>) {
  return args;
}

Now when I pass 'text' as the first parameter it auto-suggests { value: string } for the second parameter, and similarly if I pass 'prefixedText' is suggests { value?: string, prefix: string }. Also Typescript infers R from the first parameter so the consumer doesn't need to pass in the type parameter. Nice.

But as I said, I don't want to use a function, I just want the consumer to provide a tuple. I was able to come up with this:

type ComponentRef = 'text' | 'prefixedText';
type RenderPropsTuple = ComponentRef extends infer R
  ? R extends ComponentRef
    ? RenderPropMapValue<R> extends (...args: any) => JSX.Element
      ? [R, Parameters<RenderPropMapValue<R>>[0]]
      : never
    : never
  : never;

Now Typescript does complain if I define an invalid tuple BUT I don't get auto-suggestion for the 2nd element. In fact I get auto-suggestion of all the possible props for both components no matter which key I put as the first element.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source