17

tl;dr why does

const func = (a: unknown) => { if (a && typeof a === 'object' && 'b' in a) { a.b; } }; 

Give the following error message

Property 'b' does not exist on type 'object'. 

?

Edit: After looking into this more closely I have an even more minimal example. So let me rephrase my question:

How to probably narrow object type in TypeScript?

tl;dr why does

const func = (a: object) => { if ('b' in a) { a.b; } 

give the following error message:

Property 'b' does not exist on type 'object'. 

?

5
  • see stackoverflow.com/questions/51439843/unknown-vs-any Commented Jan 14, 2021 at 21:14
  • 1
    @novarx thank you. I think I have a pretty good understanding of the difference between any and unknown. If this were a: any this wouldn't even be a question. However, unknown is a good default, instead of any Commented Jan 14, 2021 at 21:17
  • maybe this will be of help typescriptlang.org/docs/handbook/advanced-types.html Commented Jan 14, 2021 at 21:39
  • regarding your edit. Don't use the object type in typescript. a better reference would be Record<string, unknown> which would describe an object with any keys with an unknown type on the value of this key. If you do this typescript can narrow down the correct property. Have a look at this playground Commented Jan 15, 2021 at 17:45
  • That makes sense. But the reason I want this is because of the initial question. Is there a way I can narrow unknown to Record<string, unknown>? Commented Jan 15, 2021 at 21:27

2 Answers 2

14

You should have a closer look at custom type guard. They basically let the compiler know, that if the condition passes, the checked value will have a specific type.

In your case:

  1. Define a custom type guard
const hasB = (value: unknown): value is { b: unknown } => { return ( typeof value === 'object' && value !== null && 'b' in value ); } 
  1. Check your value with it
const func = (a: unknown) => { if (hasB(a)) { a.b; } }; 

Playgound example

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you. I guess I knew this was possible too, but I don't understand why it's necessary in this circumstance. The compiler should be able to infer {b: unknown} without having to use a custom type guard. Is this a known limitation/future feature of TypeScript, or is there a reason the type cannot be narrowed normally?
Look at the first example of advanced types, they have this line saying if ("swim" in pet) pet.swim();, where the type of pet outside this block is Fish | Bird. If you think of unknown as the intersection of all the types (which I believe is an accurate way to think about it), then why does a similar logic not apply here?
I have updated my question to be more specific
don't think about unknown as an intersect of all types. That would be any. unknown is the opposite. until proven otherwise it's none of all possible types.
Wouldn't any be the union of all possible types?
0

I use this utility to convert an object to a dictionary:

function assertRecord(value: object): asserts value is Record<string, unknown> {} 

For example, to assert that 'a' is an object with property 'b' that is a number:

function fun(a: unknown): number { assert(typeof a == 'object' && a != null) assertRecord(a) assert(typeof a.b == 'number') return a.b } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.