TypeScript 3.x: Access properties of type unknown

josias picture josias · Jan 21, 2020 · Viewed 7.6k times · Source

I read from the TypeScript documentation, that you can't access properties from the type unknown:

// No property accesses, element accesses, or function calls

function f11(x: unknown) {
    x.foo;  // Error
    x[5];  // Error
    x();  // Error
    new x();  // Error
}

But I don't understand why? If I can assign every value including an object, why can't I access the properties?

I'm batteling the following scenario: I have an object of type any and I pass it to the Object.entries method and then call forEach.

Because it's of type any, the forEach call argument will be an array with the first element being of type string and the second one of type unknown.

In my case, that second element is an object, but I can't access its properties without converting its type, which seems wrong to do.

Here is an example, which throws a TS error (just an abstraction, I know it doesn't make sense to declare it as any in this case):

const obj: any = {
  val1: "someval",
  val2: {
    some: "some",
    object: "object",
  },
};
Object.entries(obj).forEach(el => {
  if (el[1].some) {
    console.log(el);
  }
});

error preview

This could also just be wrong typings for the Object.entries method I guess, but I'd still like to have explained, why I can't access properties of type unknown.

So as a summary my questions are:

  1. Why can't I access properties of type unknown even though type unknown can be an object?
  2. I guess there is a reason for the above question, but shouldn't the Object.entries return an array w/ element nr. 0 of type string and element nr. 1 of type any?

Answer

Alexey Kureev picture Alexey Kureev · Jan 21, 2020

I think that in order to address your question, it is important to give some context on any vs unknown. While you can find an exhaustive comparison list in the official TypeScript documentation, I think I can take some liberty and shorten the article to a few statements: any is basically a one-type-fits-all and therefore is not type-safe. By type-safe I mean you can access a runtime property of any that doesn't exist.

unknown is different. unknown is the opposite of any in this regard. It represents a type-safe version of any by stating "I won't pretend I fit everything because I don't". So unknown requires additional casting to the desired type in order to work (because it doesn't possess any properties on its own).

Now, to the actual question. Why Object.entries use unknown instead of any? Because it is safer to say "cast this unknown value to whatever you need before usage, I won't assume that I know anything about this type" than "I don't know what kind of type it is, but I'll assume that it has all possible properties of all possible types".