#How to filter items in a const assertion with Array attributes

32 messages · Page 1 of 1 (latest)

short owl
#

I want to filter based on the presence of any of the possible attr and get only the matching ones.

#

I can do it with a non-array field, say attr2: "A", attr2: "B" then I could find all B and all A but the same doesn't work for some value in the readonly array

sonic gate
#

Eh, not sure what the goal of this code is, are you trying to use y to check something about d, or the opposite?

#

Because there's this:

#

!:includes

faint roseBOT
#
retsam19#0
`!retsam19:includes`:

The type of Array.prototype.includes assumes that you're using the string to check something about the array. If you're trying to do the reverse, a modified type signature is useful:

function includes<S extends string>(haystack: readonly S[], needle: string): needle is S {
    const _haystack: readonly string[] = haystack;
    return _haystack.includes(needle)
}

declare const x: string;
if(includes(["a", "b", "c"], x)) {
    // x is "a" | "b" | "c"
}
proud zephyr
#

BTW, @short owl you don't need a link shortener, just send the full URL as its own message and the bot handles it

faint roseBOT
#
retsam19#0

Preview:```ts
const data = [
{
attr: ["A", "B", "C", "D"],
},
{
attr: ["D", "E", "F"],
},
] as const
type Data = typeof data[number]
type Y = "A" | "B" | "C"

function hasY(y: Y, d: Data) {
return d.attr.includes(y)
}```

short owl
#

I want to map the data to something like dataWithAttrD = filterDataHasAttrD(data) and type it such that type hasAttrD has all entries that contain an attr: ["D"] and anything else.

#

Similar to if the object had an attribute isLike: "A" | "B" | "C" and I wanted to filter them I'd use:

function isLike<T extends IsLike = IsLike>(isLike: T) {
  return <D extends Data>(data: D): data is D & {isLike: T} => data.isLike === isLike;
}
#

That's a predicate that I could use to filter data.

type IsLikePredicate<T extends IsLike> = ReturnType<typeof isLike<T>>;
const isLikeA = isLike("A");
function filterDataByIsLike<U extends IsLike>(filter: IsLikePredicate<U>) {
  return data.filter(d => filter(d));
}
#

In the playground link, for some reason it types includes as ReadonlyArray<T>.includes(searchElement: "D", fromIndex?: number | undefined): boolean

faint roseBOT
#
nonspicyburrito#0

Preview:```ts
type Data = { readonly attr: readonly unknown[] }

type ExtractAttr<T extends Data, A> = T extends T
? A extends T['attr'][number]
? T
: never
: never

function hasAttr<T extends Data, const A extends T['attr'][number]>(
data: T,
...```

short owl
#

So it seems like the key is type Data = { readonly attr: readonly unknown[] }

#

Wondering if I can make this type based on the const itself.

sonic gate
#

I mean you can.

#

You can replace type Data = typeof data and it will still work, although you would need to fix it to .includes(attr as never).

#

But I personally don't think it's necessary, this version works with any kind of data with attr.

short owl
#

Yea, I mean I might filter on attr: or have other fields (that might be of different types) to filter on.

#

includes(attr as never) is actually better than what I had which was (d.attr as string[]).includes(y)

sonic gate
#

Yeah, as never is a convenient hack, but you need to make sure it's correct because it's unsafe just like any other usage of as.

#

The noteworthy part of the code I've given is just the type guard narrowing, see how data is narrowed in each if depending on the attr you pass in. If you don't need that, then yeah as never will just fix your original issue.

short owl
#

I do need the narrowing of the type, that's kinda the requirement.

#

It still works with this:

faint roseBOT
#
tickleme_pink#0

Preview:```ts
//type Data = { readonly attr: readonly unknown[] }
type Data = typeof datas[number];
type ExtractAttr<T extends Data, A> = T extends T
? A extends T['attr'][number]
? T
: never
: never

function hasAttr<T extends Data, const A extends T['attr'][number]>(
...```

short owl
#

It seems better than as any but I'm not sure it is.

sonic gate
#

as never is just a slightly safer as any.

short owl
#

Let me know if I should create a new post for this, but to put this into perspective, this is what I'm doing:

faint roseBOT
#
tickleme_pink#0

Preview:ts const data = [ { key: "X", attr: ["A", "B", "C", "D"], state: "Good", }, { key: "Y", attr: ["D", "E", "F"], state: "Bad", }, { key: "Z", attr: ["D", "A", "B"], state: "Bad", }, ] as const type Data = typeof data[number] ...

short owl
#

Now you can see I'm re-mapping the const data into different forms (feel free to tell me if I'm doing something wrong in there). Filtering on state works, I'm not looking to filter on attr as well.