For example I have this data on JS
const obj = { // never have 'c' property
a: 1,
b: 2,
}
const keys = ['a', 'b', 'c'] // always 'a'|'b'|'c' --> ex: [a,b,c] / [c,b,a] / [a,c]
const firstKey = keys[0]
const selectedObj = obj?.[firstKey] || 99
Now how to do it in typescript? I have tried it but stucked with such solution
type Keys = 'a' | 'b' | 'c'
type ObjKey = Exclude<Keys, 'c'>
type Obj = Record<ObjKey, number>
const obj: Obj = {
a: 1,
b: 2,
}
const keys: Keys[] = ['a', 'b', 'c']
const firstKey = keys[0] as ObjKey // --> I don't think this is right
const selectedObj = obj?.[firstKey] || 99
I don't think this is right, because by doing so I remove the possibility of runtime check on firstKey
to have value 'c'
.
Wonder what is the correct/proper way to fix this ya?
I want to be able to access the selectedObj
, but TS scream that
Property 'c' does not exist on type 'Obj'
CodePudding user response:
You almost have it, all you need to do is to change the following line:
const firstKey = keys[0] as ObjKey
to
const firstKey: ObjKey = keys[0]
Explanation:
Your example was asserting that the firstKey
value type will be 'a'
or 'b'
, even though it may actually be some other arbitrary value. If you want TypeScript to tell you that some types are not aligning - you should use type annotation instead of an assertion. You can find a really good explanation of a more detailed explanation on the difference between the two here.
Here is a ts playground link.
CodePudding user response:
In your case, where you mentioned that the object keys won't be changed, I would suggest you to replace this:
type Keys = 'a' | 'b' | 'c'
type ObjKey = Exclude<Keys, 'c'>
with:
const keys = ['a', 'b', 'c'] as const;
type ObjKey = Exclude<typeof keys[number], 'c'>
By doing this you are creating a typescript construct called const assertion that allows you to have an unmutable object.
More docs here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions
You can then normally use a type annotation on the firstKey variable without receiving any error.
const firstKey: ObjKey = keys[0];