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'}[]
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.