Ts-toolbelt: New type request - IncludesDeep (i.e. recursive includes)

Created on 1 Apr 2020  路  5Comments  路  Source: millsp/ts-toolbelt

馃崺 Feature Request

Hi @pirix-gh 馃憢

I hope you and your family are safe during this troubling times. 馃挏

I have a feature request for you. I am in need of the behaviour of Includes, however, I would like it to recursive through the object type tree. I need this as I have some logic in a type mapper/reducer that will execute logic depending on their being an instance of a specified type on a type, or deeper within the type's nested object structure.

It would be great if it optionally supported a depth argument to avoid overly heavy computations.

Imagining a signature such as:

type IncludesDeep<O, M, match, depth>

With depth being optional, and greedy by default.

It could then be used like so:

interface Foo {
  bar: {
    qux: number;
  }
}

type FooHasNestedNumber = IncludesDeep<Foo, number>;

// with depth restriction
type FooHasNumberWithinTwoLevels = IncludesDeep<Foo, number, 'default', 2>;

Would you be open to including this API?

feature help needed needs investigation

Most helpful comment

It's out! I've opened a new Community module. Cheers!!

All 5 comments

Hi Sean, thanks, I hope you are doing well too.

This seems quite easy to do. I just have a question. Would it return true as soon as it found M on any level of depth?

If this is the case, I can write a recursive type that will take the object and make it a union N times (the depth). And check at the same time if the type M is matched. And return true whenever this is the case.

Hey @pirix-gh;

Yep, exactly that is how I imagined it. Eager return if any instance is found.

Amazing, thank you for considering this. 馃専

Hey,

I am not sure if I am going to integrate this yet. This is very specific. I think that I might create a "community" knowledge base where some types can be copy pasted but not necessarily shipped with. Because there are a few edge cases that I am not willing to cover for simplicity's sake. There are some folks that have edge cases with GraphQL too, so I think that this could land into a community category. Maybe it could be a community category shipped with the package too, what do you think?

Anyway, here's the monster:

type IncludesDeep<O, M extends any, match extends Match = 'default', limit extends Number = '10', I extends Iteration = IterationOf<'0'>> = {
    0: IncludesDeep<O extends object ? UnionOf<O> : O, M, match, limit, Next<I>>
    1: 1
    2: 0
}[
    Key<Prev<I>> extends limit  // if we go past the limit
    ? 2                         // end the loop here
    : Is<O, M, match>           // if 0 => continue, if 1 => end
]

type t0 = IncludesDeep<{
    a: {
        b: {
            x: number
        }
    }
}, number, 'default', '3'>

I'd appreciate your feedback :)

Awesome! Thanks for this @pirix-gh 馃挏

Completely agree with you that a pragmatic approach should be taken towards integrating additional APIs in order to avoid bloat.

I like the idea of having a separate location in which to showcase these additional APIs. It provides the additional benefit of being able to see how much adoption/value the community derives from them, and may even highlight good candidates for promotion into the primary package.

It's out! I've opened a new Community module. Cheers!!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

regevbr picture regevbr  路  7Comments

tonivj5 picture tonivj5  路  3Comments

asgerhallas picture asgerhallas  路  6Comments

millsp picture millsp  路  6Comments

katedoctor picture katedoctor  路  6Comments