Home > Software engineering >  Return subset of union depending on parameter in generic function
Return subset of union depending on parameter in generic function

Time:04-22

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

CodePudding user response:

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.

Playground

  • Related