Reputation: 3451
I want to somehow not have to to the ts-ignore when I use object properties referenced as string in TypeScript. That is I have a something defined like this:
const myList = ['aaa','bbb','ccc'];
const appContext = {};
for (let i=0;i<3,i++) {
appContext[myList[i]] = 'newval' + i
}
If I setup my typescript type as
interface IAppContext {
aaa: string;
bbb: string
ccc: string
I still get typescript errors. How do I handle this correctly?
Upvotes: 1
Views: 141
Reputation: 328262
It would be nice if your example code were a true minimum reproducible example showing the exact errors and with no other typos or issues, but I think I understand what's happening.
The main problem you're likely to face is that myList
is inferred by the compiler as type string[]
. This is a reasonable guess for the compiler, because often people write things like const foo = ["a","b"]
expecting to later alter the values inside the array like foo.push("c")
(a const
declaration only means you can't write foo = ...
again, not that you can't set properties on it or call state-mutating methods on it).
To help the compiler when it makes the wrong inference on a literal value like ["aaa","bbb","ccc"]
you can use a const
assertion to ask the compiler to assume that the value will stay exactly as it is and not change. If you write const myList = ['aaa','bbb','ccc'] as const
, the compiler will give myList
the type readonly ["aaa", "bbb", "ccc"]
, a tuple of length 3 whose values cannot be rewritten and whose types are the exact string literal types "aaa"
, "bbb"
, and "ccc"
, in that order.
After that your code should mostly compile without warning as long as you assert that appContext
is an IAppContext
, like const appContext = {} as IAppContext
. You can't merely annotate it like const appContext: IAppContext = {}
, because that's not true (yet). It starts off empty, which is not a valid IAppContext
. A type assertion is you telling the compiler not to worry about it, because even though appContext
doesn't start out as a valid IAppContext
, it will eventually be one after your loop runs. But keep in mind that it's your responsibility to make sure that it's properly initialized. If your myList
were just ['aaa','bbb']
, the code would still compile with no warnings and you'd be sad at runtime when you later call appContext.ccc.toUpperCase()
. A type assertion is you taking the responsibility for the correct typing when the compiler can't verify it. So take the responsibility seriously.
Anyway, here's the resulting code, with no compiler errors:
interface IAppContext {
aaa: string;
bbb: string
ccc: string
}
const myList = ['aaa', 'bbb', 'ccc'] as const; // const context to remember literals
const appContext = {} as IAppContext; // assert because it starts off empty
for (let i = 0; i < 3; i++) {
appContext[myList[i]] = 'newval' + i
}
Hope that helps; good luck!
Upvotes: 1