2

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

1 Answer 1

2

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 ...

Sign up to request clarification or add additional context in comments.

8 Comments

I knew why I get the error and in this case the js code let x = def_val[y] || def_val.a is quite clear and straightforward while the ts code is not (if I may say so)!
Does my solution helps you to fix your ts issue ? TS is more verbose than js, it is ok. since we have types
Yes your solution has helped my fix it. Thanks. I have not accepted your answer yet (only upvote) now because I am also researching if there is other way to do it. BTW, I updated my question too.
@Qiulang邱朗 sure, no problem. I made an update
Can you provide more information or some link about Record<string, string> ?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.