I'm trying to convert a generic readonly string[] into an object with known keys. But I encounter the error:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<T[number], number>'.
No index signature with a parameter of type 'string' was found on type 'Record<T[number], number>'.
Code:
class ArrayToObject<T extends readonly string[]> {
constructor (private keys : T) {}
GET_OBJECT () {
const all = {} as Record<T[number], number>
for (const index in this.keys) {
all[this.keys[index]] = index
}
return all;
}
}
What I basically want from the generic is something similar to this:
const a = ["a", "b"] as const
const o : Record<typeof a[number], number> = {
a : 1,
b : 2
}
CodePudding user response:
You can get the string union from your readonly string using type inference, like this:
const a = ["a", "b"] as const;
type StringFromReadonlyArray<T extends readonly string[]> = T extends readonly (infer U)[] ? U : string;
const tooMany: Record<StringFromReadonlyArray<typeof a>, number> = {
a: 1,
b: 2,
c: 3, // <-- Error: Type '{ a: number; b: number; c: number; }' is not assignable to type 'Record<"a" | "b", number>'.
};
const notEnough: Record<StringFromReadonlyArray<typeof a>, number> = { // <-- Error: Property 'b' is missing in type '{ a: number; }' but required in type 'Record<"a" | "b", number>'.
a: 1,
};
const justRight: Record<StringFromReadonlyArray<typeof a>, number> = {
a: 1,
b: 2,
};
That StringFromReadonlyArray utility type extracts the string union from your readonly array if possible, otherwise it resolves to the generic string type.
CodePudding user response:
It is because the type system can only know T extends readonly string[] but it cannot infer its elements' type correctly. Defining T as the element type can help the type system to infer it.
Also, the for...in statement will result in a string key value. You need to convert it into number or use Array.keys() or Array.entries() to get the numeric index.
class ArrayToObject<T extends string> {
constructor (private keys : readonly T[]) {}
GET_OBJECT () {
const all = {} as Record<T, number>
for (const [index, value] of this.keys.entries()) {
all[value] = index // 1 if you want 1-based
}
return all;
}
}
