When using Computed Property in javascript, I can write my code like this
const def_val = {a:"debug",b:"info",c:"warning"};
function work(y) {
let x = def_val[y] || def_val.a /* if y is not a or b or c */
}
But how to I make that work in typescript ?
const def_val = {a:"debug",b:"info",c:"warning"};
function work(y: string) {
let x = def_val[y] || def_val.a;
}
I got the compiler error
error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ a: string; b: string; c: string; }'.
No index signature with a parameter of type 'string' was found on type '{ a: string; b: string; c: string; }'.
I have searched SO but I can only find How to set a variable if undefined in typescript? which is not my question.
Now I changed my ts code to these, but it feels tedious compared to the original js code
function work(y: string) {
let x:string
if (y!='a' && y!='b' && y!='c') {
x = def_val.a;
} else {
x = def_val[y]
}
}
---- update ----
@captain-yossarian answer is one way of fix it, e.g. const def_val: Record<string, string> = { a: "debug", b: "info", c: "warning" };
I find the other way to fix it is to use keyof typeof
, check here https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types for further information, e.g. "In JavaScript it is fairly common to have APIs that expect property names as parameters, but so far it hasn’t been possible to express the type relationships that occur in those APIs.", exactly my case here!!
const def_val = {a:"debug",b:"info",c:"warning"};
function work(y:keyof typeof def_val) {
let x = def_val[y] || def_val.a;
}
I got this answer from How to dynamically access object property in TypeScript
CodePudding user response:
def_val
is infered by typescript as
const def_val: {
a: string;
b: string;
c: string;
}
y
argument has string
type
def_val
expects 'a' | 'b' | 'c'
as a keys. It means that typescript allows you to use only these keys with def_val
. Since string
type is much wider than 'a' | 'b' | 'c'
you are getting error.
y:string
means that it allows you to pass foo
property and def_val['foo']
is not safe since foo
does not exists in def_val
.
In order to fix it, you should provide explicit type for def_val
:
const def_val: Record<string, string> = { a: "debug", b: "info", c: "warning" };
function work(y: string) {
let x = def_val[y] || def_val.a // ok
}
IF you are not allowed to use explicit type on def_val
, you can provide def_val
as an argument to work
:
const def_val = { a: "debug", b: "info", c: "warning" };
function work<Def extends Record<string, string>>(def: Def, y: string) {
let x = def[y] || def.a
}
work(def_val, 'foo') // ok
You also can use custom typeguard:
const def_val = { a: "debug", b: "info", c: "warning" };
const isValidKey = (key: string): key is keyof typeof def_val =>
/a|b|c/.test(key)
const work = (y: string) => isValidKey(y) ? def_val[y] : def_val.a
work('foo') // ok
P.S. Here you can find documentation about using built in utility types , like Record
, Partial
, etc ...