Home > OS >  Narrowing an object of type unknown
Narrowing an object of type unknown

Time:11-19

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.

Playground link to code

  • Related