#Why does this produce a type of '{}`

1 messages · Page 1 of 1 (latest)

sharp kayak
#

Consider this sample

const x: unknown = null;
const y = x?.test;

This gives the compiler diagnostic

Property 'test' does not exist on type '{}'.

Why is the lefthand operand of the . considered to have an {} type? This seems weird to me; I'd expect this to succeed, as ?. should always net me undefined if the propery doesn't exist, no?

narrow briarBOT
#
ollien#9348

Preview:ts const x: unknown = null const y = x?.test

tame quest
#

Interesting! I don't really know but JavaScript's null is actually an object (thanks to its legacy of trying to superficially copy Java). It's essentially meant to mean "an empty object" (not to be confused with actual {})

My guess is that this is an interesting intersection between TS and JS. null is an empty object in JavaScript and {} is simply an object with no fields. But in TypeScript we use structural typing like set theory. So it likely treats null and {} the same?

The difference (if I'm right) is similar to why {} extends {} in TS but {} !== {} in JS.

Totally just a guess though and it doesn't explain why we get this result. Would love someone more knowledgeable to answer. Maybe it's something specific to when you try to actually access a field on null ?

regal tiger
#

{} doesn't mean an object, it means anything that's indexable.

tame quest
#

good point

#

maybe

  1. null is an object
  2. object is indexable
  3. ???
regal tiger
#

type Foo = 42 extends {} ? true : false will give true as well.

#

null is irrelevant here, the type of x is simply unknown

tame quest
#

42 is indexable?

regal tiger
#

After ?. x is then narrowed to a subset of unknown that's non null/undefined, and anything that's non null/undefined is indexable.

#

Yep, eg 42['toString'].

tame quest
#

very interesting. great explanation

#

thanks

regal tiger
#

But to answer OP's question, what TS is really telling you is that, x could be anything that's non null/undefined, and they don't have test property on it which you are trying to access

#

You should tell TS/validate that x really does have test property, rather than relying on the fact that indexing a non existent property gives back undefined.

#

Basically you are trying to use a JS pattern that's unfriendly to TS (or really, unfriendly to any statically typed language, you can't just access any random property you want)

tame quest
#
let y = ('test' in x) ? x.test : undefined;
regal tiger
#

Or have x typed as { test: unknown } | null, or more specific types.

#

It's a bit of an XY problem, OP should provide what they actually want to achieve.

sharp kayak
#

Thank you both!