In TypeScript, is there any way to declare that a variable is a keyof
some Record
without knowing what the keys of the Record
are?
Assume an API response returns a payload of JSON. I want to define a type for the keys of the payload so I can index through the payload and know that the value will not be undefined. Is this possible or do I have to simply check for undefined every time I index the payload object?
Example:
const payload: Record<string, ValidValue> = await myApiCall()
const prop1 = 'a'
const prop2 = 'b'
if (isValidKey(prop1, payload)) {
const value1 = payload[prop1] // the type of `value1` should evaluate to `ValidValue`
}
const value2 = payload[prop2] // the type of `value2` should evaluate to `ValidValue | undefined`
CodePudding user response:
I got it to work using a type guard function as well, but not returning key is keyof MyMap
. Instead, I had a type for the payload object of Record<string, ValidValue | undefined>
and then the type guard returns payload is Record<K, ValidValue>
where K
is the type of the key param.
You can play with it on TS Playground.
// Type alias for convenience
type Payload = Record<string, ValidValue | undefined>;
const payload: Payload = await myApiCall()
function isValidKey<K extends string>(key: K, payload: Payload): payload is Record<K, ValidValue> {
return key in payload;
}
const prop1 = 'a'
const prop2 = 'b'
if (isValidKey(prop1, payload)) {
const value1 = payload[prop1] // the type of `value1` should evaluate to `ValidValue` ✅
}
const value2 = payload[prop2] // the type of `value2` should evaluate to `ValidValue | undefined` ✅
CodePudding user response:
I think I found a solution. This appears to work as expected in the TS playground.
const myMap = [1,2,3].reduce((p, n) => ({ ...p, [Math.random()]: n }), {})
type MyMap = typeof myMap
console.log('myMap', myMap)
function isKnownKey(key: string, thing: MyMap): key is keyof MyMap {
return Object.keys(thing).includes(key)
}
const knownTypes = ['a', 'b']
const prop1 = Object.keys(myMap)[0]
const prop2 = 'b'
if (isKnownKey(prop1, myMap)) {
const value1 = myMap[prop1]
console.log('value1', value1)
}
const value2 = myMap[prop2]
/*
TS Error:
Element implicitly has an 'any' type because expression of type '"b"' can't be used to index type '{}'.
Property 'b' does not exist on type '{}'.(7053)
*/
However, one thing is concerning and that I don't understand: value1
evaluates to type never
. Yet there's no TS error. What's going on here?