Home > Back-end >  Typescript when accessing object that doesn't have such property (possibly not exist)
Typescript when accessing object that doesn't have such property (possibly not exist)

Time:06-06

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];
  • Related