#Keep const assertion of array through calls to filter

1 messages · Page 1 of 1 (latest)

keen raftBOT
#
tickleme_pink#0

Preview:```ts
const data = [
{a: 1, b: 1},
{a: 2, b: 2},
{a: 2, b: 1},
{a: 2, b: 3},
{a: 5, b: 2},
] as const
type Data = typeof data[number]
type HasA<D extends Data, N> = D extends D
? N extends D["a"]
? D
: never
: never

const aIs2 = <D extends Data>(d: D): d is HasA<D, 2> =>
d.a === 2
...```

tight vessel
#

well, you could, but now you're just using ts to do runtime stuff

#

it's really not meant for that

#

also, the const assertion itself wouldn't last through any transformations, you're asking for a tuple type for the result

#

are you just trying to do this for fun, or are you trying to use this for something?

vestal goblet
#

I'm trying to get an API to filter some data (which is const asserted) while keeping the benefits of const assertion.

tight vessel
#

what benefits exactly?

vestal goblet
#

Auto-completion and allows to precisely know the type of a specific entry so you can use it as an input param of a function requiring that specific thing.

tight vessel
#

i mean, you usually don't need to know the exact type of a specific entry. what function takes an { a: 1 } but not an { a: 2 }?

#

you're trying to get ts to know all the runtime values, that's not what it's designed to do, and you'll be fighting ts to do it

#

if your usecase is more specific and actually differentiates shapes, then could you show an example of that?

vestal goblet
#

A user may be interested in only data that has a == 2 so they'll use const mydata = helper.aIs2() but then they use function(x: {a: 2, b:2}) and they won't be able to do mydata[0].

tight vessel
#

that's not really a reasonable usecase

#

that should just narrow the array element directly

#

or extract it first and then narrow it

vestal goblet
#

The problem is heavily abstracted though.

tight vessel
vestal goblet
#

That would be a very lengthy process.

tight vessel
#

you can't describe it in any way that isn't insanely genericized?

#

either way, just narrowing on the array directly would be easier

tight vessel
#

it doesn't need to be a tuple

keen raftBOT
#
that_guy977#0

Preview:```ts
...
const allIs2 = data.filter(aIs2)

allIs2.forEach(aOnly2)

function aOnly2(x: {a: 2}) {
return x.a === 2
}
...```

tight vessel
#

this works just fine without allIs2 being a tuple, since the elements are still narrowed

hybrid iron
#

I don't quite get the use case either.

#

The only way for you to know allIs2[0] is { a: 2, b: 2 }, is if you already know what data is before filtering (or at least know enough to know what the result is going to be before filtering)

#

But if you already know what data is, what's the point of filtering at all just to use allIs2[0]? You might as well just use data[1] directly since you already know data[0] is going to get filtered away.

hybrid iron
#

If you really want though, you can do it with a recursive type:

keen raftBOT
#
type Data = [
    { a: 1; b: 1 },
    { a: 2; b: 2 },
    { a: 2; b: 1 },
    { a: 2; b: 3 },
    { a: 5; b: 2 },
]

type FilterByA<T, A, R extends unknown[] = []> = T extends [infer D, ...infer T]
    ? FilterByA<T, A, [...R, ...(D extends { a: A } ? [D] : [])]>
    : R

type Result = FilterByA<Data, 2>
//   ^? - type Result = [{
//       a: 2;
//       b: 2;
//   }, {
//       a: 2;
//       b: 1;
//   }, {
//       a: 2;
//       b: 3;
//   }]
hybrid iron
#

But what's been said about still stands, that I don't see a use case for this.

vestal goblet
#

you can't describe it in any way that isn't insanely genericized?

Consider a library providing static data about all EC2 instances for example. A map with all instances by name is useful to select an instance to pass as a parameter. But suppose a user needs to use only Intel instances of 3rd generation available in us-east-1, they'd want to remap the static data to their filter instead.