'Return subset of union depending on parameter in generic function
Given the classic discriminated union:
interface Square {
kind: 'square';
width: number;
}
interface Circle {
kind: 'circle';
radius: number;
}
interface Center {
kind: 'center';
}
type Shape = Square | Circle | Center;
How can I type the following filter so that the return type is narrowed down to all Shape that have a kind in kinds.
function filterShape(kinds: Array<Shape["type"]>, shapes: Array<Shape>): Array<?> {
return shapes.filter((shape) => kinds.includes(shape.kind))
}
I tried this, but the return type (unsurprisingly) always ends up being Array<Shape>.
function filterShape<S extends Shape>(kinds: Array<S["kind"]>, shapes: Array<Shape>): Array<S> {
for instance the type of s in the call below is Array<Shape>. I would like Array<Center | Circle>.
const s = filterShape(["center", "circle"], shapes)
You'll find a TS playground with the above code at https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgMoEcCucooN4BQyxyA1qACYBcyA5AM5Y4S0DcBAvgQaJLIigDCwKAgA2+IiXIhqdBCPEt2XHuGjwkyQRHVRkhEmUo1aSPW07cCYAJ4AHFKgAWcR8gC8aJrmQAfbUUJf21dPnYCGEwQBDBgAHsQZBhgMT4XNwgAHlRkCAAPSFl6NFdHAD4AChkKehoAQSgoOFscgG0AIhqOgF1ygBpkejKIOuRG5taMioBKBqaWnPKDKVwwTCgk4cz6ADoUtOhKyu3HGc9lmr3QcUwKUZOR3ZqZ87gSicXUcpVuBET6GAhiMxp8piNll42ngajQOoxsLgOhxBjCTMgOgpRBJkajYRjzHxcQZ8ZiwtBkT0Iv8QIChp5kql0iNKp1CRTBpighBeoNTqMZkA
Solution 1:[1]
This is possible using Extract:
function filterShape<Kinds extends Shape["kind"][]>(kinds: Kinds, shapes: Array<Shape>): Array<Extract<Shape, { kind: Kinds[number] }>> {
Extract all members of the union Shape that are assignable to type { kind: Kinds[number] }.
Kinds is an array of kinds of shapes we want. So Kinds[number] gives us a union of the kinds we want.
const s = filterShape(["center", "circle"], shapes);
// ^????????? type is `(Circle | Center)[]`
Note that this only works when kinds is a compile-time constant value, as it is in your question. This would not work:
const kinds: Shape["kind"][] = ["center", "circle"];
const s2 = filterShape(kinds, shapes);
// ^^???????? type is `(Square | Circle | Center)[]` (basically, `Shape[]`)
But with compile-time constant values, this is very handy.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | T.J. Crowder |
