Home > OS >  Typescript generic array.find() method that extracts result type from the input tuple
Typescript generic array.find() method that extracts result type from the input tuple

Time:11-27

Let's say I have a widget hierarchy like this:

interface IBaseWidget {
  type: string;
}

interface IButton extends IBaseWidget {
  type: 'button';
  title: string;
}

interface ICheckbox extends IBaseWidget {
  type: 'checkbox';
  checked: boolean;
}

type IWidget = IButton | ICheckbox;
type IWidgetType = IWidget['type'];

I want to write a method to find all widgets of a given type.

How do I type this function so that it can "extract" the correct member types from the input array?

Basically:

function findByType(widgets: IWidget[], type: IWidgetType) {
  return widgets.filter(x => x.type === type);
}

// How to make this IButton[] ?
const buttons = findByType([], 'button');

Bonus points if this can be absolutely generic, in that I don't even need to know about IWidget, but the tuple variants are extracted straight from the input array type.

Eg. I can do something like:

const input: ({type: 'A'} | {type: 'B'} | {type: 'C'})[] = [];
const result = findByType(input, 'B'); // result: {type: 'B'}[]

Sandbox: https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgJICE4GcIHVgAmA5hGMgN4BQyyYAngA4QBcyWYUoRA3JQL6VKoSLEQoMAVzBgA9iGQQAHpBAEsaTDnzFSFarUYtkAcgBGU2SGO8aYYGAA2R9pxA9 g4dHhI0AYQALCAQAa1MZRQVlCFV1DGw8QhIyKltDVmMEINDwxWt9LOCQiAJWcJknOBBeAUp6JjRtZOQAXg0LOWQAH39ssIjeevEm0gAVQ1bGpNIAbWMh4wBdXkoYCRAEO06YUAJ0OnGmAAoAd2mwLFZUEbAZxYAaAyYrm8OIAEo9GihSCSh5M46C4AOh2DhER0iLQAfMhFMChq0Wm0hu8aoIAPQY5AACRkJ1oMmQAFs4MVaAFgHF0B0QHdkAB SgIOTsZDmaSsyY7VT7N5HO6PMy04xooA

CodePudding user response:

I used this excellent previous answer as inspiration for an adapted version that should work well for your use case:

interface IBaseWidget {
  type: string;
}

interface IButton extends IBaseWidget {
  type: "button";
  title: string;
}

interface ICheckbox extends IBaseWidget {
  type: "checkbox";
  checked: boolean;
}

type IWidget = IButton | ICheckbox;

type FilterWidgetByType<
  W extends IWidget,
  Type extends string
> = W extends any ? (W["type"] extends Type ? W : never) : never;

function findByType<T extends IWidget["type"]>(widgets: IWidget[], type: T) {
  return widgets.filter(
    (widget): widget is FilterWidgetByType<IWidget, T> => widget.type === type
  );
}

// How to make this IButton[] ?
const buttons = findByType([], "button");
const checkboxes = findByType([], "checkbox");

Here is the TS playground link.

  • Related