for example, a function to map object keys to another ones.
function mapKeys<O extends Record<string, unknown>, K extends keyof O, KM extends Record<K, string>>(obj: O, keyMap: KM) {
const r: Record<KM[keyof KM], O[K]> = {}
for (const k of Object.keys(obj)) r[keyMap[k]] = obj[k]
return r
}
the function will meet the error: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<KM[keyof KM], any>'. No index signature with a parameter of type 'string' was found on type 'Record<KM[keyof KM], any>'.(7053)
As I know, it's because the property value type of keyMap
is string
instead of string literal.
const x = mapKeys({a: 1}, {a: 'b'} as const)
/*
'as const' instructs the compiler it's a literal,
so here the `x.b` will correctly inferred to be `string`.
*/
But how can I make the compiler infer the object property values type to be the string literal instead of string when declare types? (when the source code is Javascript, it's not possible to use as const
assertion on the value)
P.S. the keyMap
is not sure. (So enum
or union
is not work.)
SOLVED:
To make the compiler infer the mapped keys in the output object, the key is to narrow keyMap
with Partial<>
:
function mapKeys<O extends Record<string, unknown>,
K extends keyof O,
KM extends Partial/* narrow keyMap */<Record<K, string>>>(obj: O, keyMap: KM) {
const r: Record<KM[keyof KM], O[K]> = {}
for (const k of Object.keys(obj)) r[keyMap[k]] = obj[k]
return r
}
// here `x.b` will be inferred (as `a`'s type) without using `as const`
const x = mapKeys({a: 1}, {a: 'b'})