Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

transform named tuple to object

Tags:

typescript

is there any way to transform named/labeled tuple

type Tup = [a: string, b: number];

to object

type Obj = {
     a: string;
     b: number;
}

last attempt does not wotrk

type Map1<Container extends unknown[]> = {
     [K in keyof Container]: Container[K];
};

type Map2<Container extends Record<PropertyKey, any>, Replace> = {
     [K in keyof Container]: Replace;
};

let c1 : Map1<Tup> = {
     'a' : 's',
     'b' : 1,
};

let c2 : Map2<Tup> = {
     'a' : 's',
     'b' : 1,
};

thanks

like image 710
Andikac Avatar asked Apr 06 '26 16:04

Andikac


1 Answers

TypeScript's support for labeled tuple elements as implemented in microsoft/TypeScript#38234 intentionally does not provide any way to observe these labels in the type system. From the implementing pull request:

Names do not affect assignability in any way, and just exist for documentation and quickinfo.

Also see this comment in a related issue where the dev lead for the TS team says:

[Tuple labels] are purely for display purposes (similar to parameter names in function types)

The tuple type [a: string, b: number] and the tuple type [c: string, d: number] are indistinguishable from the point of view of the type system. The only difference between them is how they are displayed in an IDE or output as documentation. They are more like comments than code.

If [a: string, b: number] and [c: string, d: number] are the same type, then any type function TupleToObject<T> that turns [a: string, b: number] into {a: string, b: number} would also need to turn [c: string, d: number] into {a: string, b: number}.

So there's really no hope of implementing what you're looking for. Tuple labels are intentionally hidden from the type system. There's just no connection between tuple labels and string literal types.


Instead, the only possible way forward is for you to manually specify the string literal keys you'd like to use for each element of the input tuple. So the unworkable TupleToObject<[a: string, b: number]> becomes something like TupleToObject<[a: string, b: number], ["a", "b"]>. You can see how this doesn't care about tuple labels anymore; TupleToObject<[foo: string, bar: number], ["a", "b"]> would by necessity produce the same type.

Okay, so how would someone implement TupleToObject? Here's one potential way:

type TupleToObject<T extends readonly any[],
  M extends Record<Exclude<keyof T, keyof any[]>, PropertyKey>> =
  { [K in Exclude<keyof T, keyof any[]> as M[K]]: T[K] };

It takes two type parameters: T is a meant to be the tuple type, while M is meant to be a tuple type of at least the same length whose values are keys. The extends readonly any[] and extends Record<Exclude<keyof T, keyof any[]>, PropertyKey>> generic constraints more or less enforce this.

Then for each literal numeric-like key of the T tuple, we use key remapping to replace that key with the corresponding entry from M. The only weird bit is that instead of iterating over keyof T, we use Exclude<keyof T, keyof any[]>. Tuple types are arrays, and will contain array method names like "map". We don't want to worry about "map", so the Exclude<T, U> utility type lets us say "all those keys from T which are not general array keys". For tuples this tends to leave just "0", "1", etc.


Let's test it out:

type Tup = [a: string, b: number];

type Obj = TupleToObject<Tup, ["a", "b"]>
/* type Obj = {
    a: string;
    b: number;
} */

Looks good!

Playground link to code

like image 131
jcalz Avatar answered Apr 08 '26 15:04

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!