3

I have this simple function that checks if an unknown value looks like a Date object:

function looksLikeDate(obj: unknown): obj is { year: unknown; month: unknown; day: unknown } {
    return (
        obj !== null && typeof obj === "object" && "year" in obj && "month" in obj && "day" in obj
    );
}

But I get the following error for the "year" in obj part of the code:

Object is possibly 'null'. (2531)

When I switch obj !== null and typeof obj === "object" the error goes away: TS Playground Link

Isn't this strange? Can anybody explain this to me?

9
  • Does this answer your question? Typescript property type guards on unknown Commented Oct 26, 2021 at 9:18
  • 2
    @captain-yossarian: That doesn't explain that typeof null returns 'object', which is the problem here. Commented Oct 26, 2021 at 9:20
  • Is there a reason your type-guard doesn't ensure that year, month, and day are typed as number? If they can be string-or-number then you should use day: string | number. Commented Oct 26, 2021 at 9:23
  • @Dai yes, they could be strings. Commented Oct 26, 2021 at 9:24
  • 2
    This is because obj !== null does not narrow the obj to the object type. After this guard, obj is still unknown. TS does not support negation types, hence it is impossible to express unknown | not null. Whereas, typeof obj === object narrows the obj to specific union object | null. After that you are allowed to extract null with another one typeguard Commented Oct 26, 2021 at 9:28

1 Answer 1

2
typeof null === 'object'

If you do the typeof check after the null check, the obj can be null, even though you checked for null before, but the TS compiler is a little naïve.

Those type guards are a little fragile in that sense, as in your example, typeof obj === 'object' changed the type from "not null" to object | null

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

4 Comments

Yea, it was, @KrisztiánBalla :D
On that first line? That's intentional. The type of null is 'object'.
Thanks for the explanation. This is not how this should work imho. (A && B) and (B && A) should have the same result.
Each subsequent type guard changes the type of the value, after it. Those type guards don't take earlier type guards into consideration. typeof <something> === 'object' will always result in that something being typed object | null, which is why you need the null check after it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.