Note: TypeScript is invoked with tsc --strict
for all code shown below.
Given a singleton object o
:
const o = {
foo: 1,
bar: 2,
baz: 3,
};
If I have a string value (say from user input) that cannot be known at compile time, I want to safely use that string to index o
. I don't want to add an index signature to o
because it is not dynamic or extensible—it will always have exactly these three keys.
If I try simply using a string to index o
:
const input = prompt("Enter the key you'd like to access in `o`");
if (input) {
console.log(o[input]);
}
TypeScript reports this error, as expected:
error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ foo: number; bar: number; baz: number; }'.
No index signature with a parameter of type 'string' was found on type '{ foo: number; bar: number; baz: number; }'.
console.log(o[input]);
~~~~~~~~
If I try changing the condition to verify at runtime that the value of input
is a key of o
:
if (input && input in o) {
console.log(o[input]);
}
This is not enough to convince TypeScript that the operation is safe, and the compiler reports the same error.
However, if I wrap the same logic for checking if input
is a key of o
in a custom type predicate, then the program compiles and works as expected:
function isKeyOfO(s: string): s is keyof typeof o {
return s in o;
}
if (input && isKeyOfO(input)) {
console.log(o[input]);
}
My question is: Are there any other ways to narrow a value of type string
into a value of type keyof typeof o
? I'm hoping there is another approach that is more concise. I'd also be interested in a generic solution to the use case of indexing an object with a dynamic string so that I don't need a type predicate that is specific to o
.