Reputation: 765
I am trying to make sure the object has all of the keys I expect it to have (.result.items
), the only problem is I get the following error:
Property result does not exist on type 'object'
on:
typeof (object.result as Record<string, any>).items === "object"
I do not understand this error because it seems that I already proved that object.result
does exist with my previous line and it should be of type object.
let objectKeyCheck = (
object: unknown
): object is { result: { items: object[] } } => {
return (
!!object &&
typeof object === "object" &&
!!(object as Record<string, any>).result &&
typeof (object as Record<string, any>).result === "object" &&
!!(object.result as Record<string, any>) &&
typeof (object.result as Record<string, any>).items === "object"
);
};
Upvotes: 1
Views: 621
Reputation: 329773
The problem here is that when you use a type assertion on an expression, it "shields" the expression from any narrowing which might normally happen. For example:
function foo(x: string | number) {
if (typeof x === "string") {
console.log(x.toUpperCase()); // okay
}
if (typeof (x as string) === "string") {
console.log(x.toUpperCase()); // error!
}
}
In the first block we are performing a typeof
type guard on x
, which allows us to narrow it from string | number
to just string
. But in the second block we aren't guarding x
, we're guarding x as string
... which has no effect whatsoever on x
.
Type assertions and narrowings don't really mix.
In order to fix this, I'd stay away from type assertions, and just use the support added in TypeScript 4.9 for unlisted property narrowing using the in
operator:
let objectKeyCheck = (
object: unknown
): object is { result: { items: object[] } } => {
return (
!!object &&
typeof object === "object" &&
"result" in object &&
!!object.result &&
typeof object.result === "object" &&
"items" in object.result &&
!!object.result.items &&
typeof object.result.items === "object"
);
};
Once we narrow object
from unknown
to object
, then we check "result" in object
and suddenly the compiler will treat object.result
as a valid index, and the property type starts off as unknown
. Then we can narrow object.result
to type object
, and do likewise with object.result.items
.
Note that the above relies on a feature added in TypeScript 4.9. If you're using an earlier version there are almost certainly other approaches which also fix the problem, but I'm not going to digress here with exploring them as they are already out of date and will become less useful over time. The recommended thing to do is to upgrade TypeScript, and those who need code that targets a particular older version, might want to ask a new question with explicit requirements around language versioning.
Upvotes: 1