I'm trying to create a function createCells
that takes an array of strings and returns an object whose properties depends on the index of items in the array
For example the function createCells(['10', '12', '13'])
would return an object with properties {Cell0, Cell1, Cell2}
So far I've been able to create one where the object returned is based on the value of items in the array
const createCells = <T extends `${number}`[]>(
args: T,
) => {
return args.reduce(
(prev, curr) => ({
...prev,
[`Cell${curr}`]: () => { },
}),
{}
) as {
[key in typeof args[number] as `Cell${key}`]: (
) => void;
};
};
but this way the function createCells(['10', '12', '13'])
would return an object with properties {Cell10, Cell12, Cell13}
For what I'm trying to achieve, the function would be
const createCells = <T extends `${number}`[]>(
args: T,
) => {
return args.reduce(
(prev, curr, index) => ({
...prev,
[`Cell${index}`]: () => { },
}),
{}
)
};
How do i use typescript to indicate the type of the object returned by the function
CodePudding user response:
// it is possible to get the length of a tuple and therefore you can create a counter
type Increment<T extends unknown[]> = [...T, unknown]
// create a helper type to do something based on the value and the index
type CellType<CellValue, Index extends unknown[]> = { [K in `Cell${Index["length"]}`]: CellValue } // do something based on the value or the index
// Iterate over your tuple and intersect the result of CellType<~,~> to create the target object
type CreateCells<T extends unknown[], Index extends unknown[] = []> =
T extends [infer Head, ...infer Rest]
? CellType<Head, Index> & CreateCells<Rest, Increment<Index>>
: {}
// This type gets infers the value without using required and
// prettifies the intersection {a:"a"} & {b:"b"} => {a:"a",b:"b"}
export type Narrowable = string | number | bigint | boolean;
export type Narrow<A> =
| (A extends Narrowable ? A : never)
| (A extends [] ? [] : never)
| {
[K in keyof A]: A[K] extends Function ? A[K] : Narrow<A[K]>;
};
const createCells = <T extends `${number}`[]>(
// Narrow allows us prevent the widening
args: Narrow<T>,
): Narrow<CreateCells<T>> => {
// for narrow we have to cast the argument
return (args as unknown as string[]).reduce(
(prev, curr, index) => ({
...prev,
[`Cell${index}`]: () => { },
}),
// yeah, any here to make our ts-compiler happy
// I think this is a common practice if you are creating an object dynamicly
{} as any
)
};
type Test = Narrow<CreateCells<["1", "3"]>>
const x = createCells(["1", "2", "3", "124214"]) //{ Cell0: "1"; Cell1: "2"; Cell2: "3"; Cell3: "124214";}
const z = createCells(["1", "2", "3", "124214asd"]) //invalid