#Define array type where the type of each element relates to the type of the previous element

33 messages · Page 1 of 1 (latest)

tidal marlin
#

Hi, I want to define an array type which would be the logical extension of the following, but for arrays of arbitrary length.

type Func<X,Y> = (obj:X) => Y;
type Arr<A,B,C,D,E,...> = [Func<A,B>,Func<B,C>,Func<C,D>,Func<D,E>...]

Regardless of whether this is the best solution to my specific problem, I am curious if typescript supports this kind of type.

rugged cloak
#

you can do it using recursion over a tuple

main heraldBOT
#
that_guy977#0

Preview:```ts
type Func<X, Y> = (obj: X) => Y
type Arr<T extends readonly unknown[]> = T extends [
infer First,
infer Second,
...infer Rest
]
? [Func<First, Second>, ...Arr<[Second, ...Rest]>]
: []

type X = Arr<[number, string, number, bigint, boolean]>
// ^?```

rugged cloak
#

something like this

tidal marlin
#

Excellent. Thanks for that, I'll have a play

main heraldBOT
#
that_guy977#0

Preview:ts type Func<X, Y> = (obj: X) => Y type Arr<T extends readonly unknown[]> = T extends [ infer First, infer Second, ...infer Rest ] ? [Func<First, Second>, ...Arr<[Second, ...Rest]>] : T extends [infer First, ...infer Rest] ...

rugged cloak
#

this absolute behemoth also supports variable length inputs

tidal marlin
#

hmm, actually, I think I'm still stuck

#

How to explain my conundrum...

#
type Getter<X,Y> = (obj:X) => Y;

function member<O extends {}, M extends keyof O>(m: M): Getter<O, O[M]> {
    return (obj: O) => obj[m];
}
function element<A extends Array<unknown>, E extends keyof A & number>(e: E): Getter<A, A[E]> {
    return (arr: A) => arr[e];
}

function get<X,Y>(obj: X, ...getters: [...]): Y {

}
#

the implementation would just chain the getters together to get the final value

#

I could easily do it with nested getters, but the ergenomics get ugly

#

I dont want something like `member(element(member(member(obj, 'a'), 'b'), 1, 'c')

#

I would prefer something like get(obj, member('a'), member('b'), element(1), member('c'))

rugged cloak
#

why can't you just do obj.a.b[1].c

tidal marlin
#

The example is contrived

rugged cloak
#

fair enough

tidal marlin
#

the real problem is more complex

rugged cloak
#

i think a mapped tuple would do better maybe?

tidal marlin
#

Its also something of an interesting problem to me 🙂

#

mapped tuple, like what you did or something else?

rugged cloak
#

my example doesn't have mapped tuples

#

one sec

#

thonk i think this might need to be recursive for type safety

tidal marlin
#

oh yeah, sorry, I've not done any advanced typescript for like a year which is why im struggling

rugged cloak
#

i have no idea what's going on here thinkies

#

well fyi i guess, mapped tuples are written like mapped types and let you.. well, map tuples

tidal marlin
#

oh, I know what they are. I just confused the geneal term "mapping" with the specific typescript term of mapped tuples

#

not to say that I'm great at using them though. more I have used on occasion and know they exist

tidal marlin
#

Looks like I can solve it reasonably by only allowing a finite maximum number of elements

#

a bit yuckky, but quite workable

#
function get<A>(obj: A): A;
function get<A,B>(obj: A, ...getters: [Getter<A, B>]): B;
function get<A,B,C>(obj: A, ...getters: [Getter<A, B>, Getter<B, C>]): C;
function get<A,B,C,D>(obj: A, ...getters: [Getter<A, B>, Getter<B, C>, Getter<C, D>]): D;
function get<A,B,C,D,E>(obj: A, ...getters: [Getter<A, B>, Getter<B, C>, Getter<C, D>, Getter<C, E>]): E;

function get<A>(obj: A, ...getters: Array<Getter<unknown, unknown>>): unknown {
    let result: any = obj;
    for (const getter of getters)
        result = getter(result);
    return result;
}
mild moon
#

@tidal marlin look like more of a chained list than an array to me