I have read multiple posts and issues on here with respect to narrowing an object
of type unknown
in TypeScript. But I have not found this specific question or solution.
Most solutions I find state to do something like this.
const result: unknown = {
movieName: "test",
};
if (
typeof result === "object" &&
result !== null &&
"movieName" in result &&
typeof result.movieName === "string" // <-- Error Here
) {}
The error states
Property 'movieName' does not exist on type 'object'
How do I narrow it so that it knows that the unknown
thing is an object
which contains the property movieName
? How do I access result.movieName
for an unknown
type?
edit: perhaps it's not possible? and the recommendation in the comments below of declaring it as a Record<string, unknown> may be the only way per this GitHub request?
CodePudding user response:
This is currently a missing feature in TypeScript. See microsoft/TypeScript#21732 for the relevant feature request. Right now when you use the in
operator as a type guard like k in result
, it only really does any meaningful narrowing if the type of result
is a union where some members are known to have the key k
and others are not. It does not assert the existence of a property with key k
. So in your case, where the type of result
is just object
(narrowed from unknown
), nothing meaningful happens.
The workaround I sometimes use is to write my own user-defined type guard function which behaves the way I want in
to behave. For example:
function hasKey<K extends string, T extends object>(
k: K, o: T
): o is T & Record<K, unknown> {
return k in o;
}
Now, instead of writing k in result
, I write hasKey(k, result)
:
if (
typeof result === "object" &&
result !== null &&
hasKey("movieName", result) &&
typeof result.movieName === "string" // okay
) {
result.movieName.toUpperCase(); // okay
}
This works how you want, and is arguably the closest you can get to type safety here, at least until something is done to address microsoft/TypeScript#21732 natively.