Given the function
function invert(arr: string[]) {
let result = {};
arr.forEach((x, i) => {
result[x] = i;
})
return result; // insert type cast here
}
What I'm basically looking for, is a way for intellisense in VSCode to tell me exactly what props are available in the resulting object, so given const example = invert(['foo', 'bar', 'baz'])
The auto-completion should show me this:
My current approach involves casting the result as {[K in T[number]]: K}
where T
is an alias for typeof arr
, but this approach doesn't give me any auto-completion.
What do I write to get TypeScript to do show me this stuff?
EDIT: It should be noted that the interface {[name]: number}
is merely a simplified example (for NDA reasons), the main question of how to map an array to property names is still the core of it all.
CodePudding user response:
If you're happy to define your array using as const
this is a possible solution
type ArrayToNumberMapType<K extends string, A extends readonly K[]> = {[K in A[number]]: number}
function invert<K extends string, A extends readonly K[]>(arr: A): ArrayToNumberMapType<K, A> {
let result: ArrayToNumberMapType<K, A> = {} as ArrayToNumberMapType<K, A>;
arr
.forEach(
(x: K, i: number) => {
result[x] = i;
}
)
return result;
}
const someArray = ['foo', 'bar', 'baz'] as const
const mappedObject = invert(someArray)
mappedObject. // .foo, .bar or .baz
CodePudding user response:
As the comments already noted, one way to do this is to make invert
generic. Currently arr
is only a string[]
. That's why your mapped type does not work.
function invert<T extends string>(arr: T[]): Record<T, number> {
let result = {} as Record<T, number>;
arr.forEach((x, i) => {
result[x] = i;
})
return result
}
This simply generic implementation produces a Record<T, number>
where T
consists of the keys inside the array.
const example = invert(['foo', 'bar', 'baz'])
// const example: Record<"foo" | "bar" | "baz", number>
This works but we could still improve this a bit. As currently, the keys are only typed as number
even though we know exactly which number they have.
function invert<T extends string[]>(arr: [...T]): {
[K in keyof T & `${bigint}` as T[K] & string]: K extends `${infer N extends number}` ? N : never
} {
let result = {} as any;
arr.forEach((x, i) => {
result[x] = i;
})
return result
}
In this implementation, we map over the elements of T
directly and filter out the number keys. Then we convert the index K
to a number.
This results in the following return type:
const example = invert(['foo', 'bar', 'baz'])
// const example: {
// foo: 0;
// bar: 1;
// baz: 2;
// }