Skip to main content

Typescript when accessing object that doesn't have such property (possibly not exist)

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'
Answer

All you need to do is to change the following line:

type Obj = Record<ObjKey, number>

to

type Obj = Partial<Record<Keys, number>>

Explanation:

In your question, you expressed a desire for TypeScript to raise a warning/error that the accessed obj?.[firstKey] property may not be defined which would force the developer to add a runtime check on it. In the example provided, we can see that all of the obj properties are defined and TypeScript will immediately complain about its use during runtime. The way to get around is to make all of the obj properties optional by using the Partial utility type. This will tell TypeScript that the property being accessed may or may not be defined, hence the developer will need to add code to use it.

Here is a ts playground link.

Comments