How to use type predicates to give type to an value of type unknown?

Viewed 345

I have a type predicate function that is expected to give type to a JSON object

interface Duck {
  canQuack: true
}

function isDuck(duck: unknown): duck is Duck {
  if (typeof duck !== "object" || ! duck) return false
  return duck.canQuack === true
}

But typescript complains that canQuack doesn't exit on object.

How do I do type check on an object with type unknown?

2 Answers

You'll need an additional type guard

interface Duck {
  canQuack: true;
}

function isDuck(duck: unknown): duck is Duck {
  if (typeof duck !== "object" || !duck) return false;
  const canQuackGuard = (o: object): o is { canQuack: unknown } =>
    "canQuack" in o;
  return (
    canQuackGuard(duck) &&
    typeof duck.canQuack === "boolean"
  );
}

If you look at the typescript documentation (https://www.typescriptlang.org/docs/handbook/advanced-types.html#using-type-predicates), they recommend casting to the the type and then checking the value. Just make sure the cast is safe, so that you don't get runtime errors (that's why we first check it's an object). In your case, the solution would be.

interface Duck {
  canQuack: true
}

function isDuck(duck: unknown): duck is Duck {
  // Check that the candidate is an object
  return duck != null && (typeof duck === 'object' || typeof duck === 'function') 
  // check that it has canQuack
    && (duck as Duck).canQuack === true;
}

Edit: made the object check actually work, copied the "is object" check from lodash https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L11745

Related