I have some JSON data that I've strongly typed with an as const
assertion.
const myList = [
{ type: 'uint256'},
{ type: 'address'}
] as const;
Now I want to convert this to the following tuple type:
[number, string]
Basically, if the type is "address" it should resolve to string
.
If the type is a "uint256", it should resolve to number
.
I intend to use this type to create arrays of data that conform to this schema, eg:
const foo: ConvertToTuple<typeof myList> = [1, 'asdf'];
I'm struggling to write a generic that can do this. Here's where I'm at so far:
type ConvertToTuple<
DataObjects extends readonly { type: "address" | "uint256" }[]
> = DataObjects extends readonly [infer CurrentObject, ...infer RestObjects]
? CurrentObject["type"] extends "address"
? [string, ...ConvertToTuple<RestObjects>]
: CurrentObject["type"] extends "uint256"
? [number, ...ConvertToTuple<RestObject>]
: never
: [];
I don't think I'm using infer
correctly here. Typescript is throwing a couple of errors:
Type '"type"' cannot be used to index type 'CurrentObject'
and
Type 'RestObjects' does not satisfy the constraint 'readonly { type: "address" | "uint256"; }[]'.
Type 'unknown[]' is not assignable to type 'readonly { type: "address" | "uint256"; }[]'.
Type 'unknown' is not assignable to type '{ type: "address" | "uint256"; }'.
Any help untangling this is much appreciated!
CodePudding user response:
TypeScript added extends
constraints on infer
type variables in version 4.7, so you can simplify the recursive mapped type utility like this:
type ListType = "address" | "uint256";
type ListElement = { type: ListType };
type Transform<Tup extends readonly ListElement[]> =
Tup extends readonly [
infer H extends ListElement,
...infer R extends readonly ListElement[]
]
? [
H["type"] extends "address" ? string : number,
...Transform<R>
]
: Tup;
const myList = [
{ type: "uint256" },
{ type: "address" },
] as const satisfies readonly ListElement[];
type Foo = Transform<typeof myList>;
//^? type Foo = [number, string]
const foo: Foo = [1, "asdf"]; // ok
If your enumeration of possible string literal types ends up growing, you can also use a type mapping of string literals to their corresponding types like this:
type TransformTypeMap = {
address: string;
uint256: number;
};
type Transform<Tup extends readonly ListElement[]> =
Tup extends readonly [
infer H extends ListElement,
...infer R extends readonly ListElement[]
]
? [TransformTypeMap[H["type"]], ...Transform<R>]
: Tup;