Skip to content

"Using type predicates" documentation promotes lying to the type system #63421

@ilyash-b

Description

@ilyash-b

Acknowledgement

  • I acknowledge that issues using this template may be closed without further explanation at the maintainer's discretion.

Comment

Using type predicates documentation provides this example:

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

This promotes lying to the type system. At the point of as, we don't know the type. Still, we tell the compiler "trust me, it's a Fish". This is semantically incorrect.

Why is this a problem? During code reviews, I was wondering about the usage of as before we know the type. Sometimes, it takes more elaborate forms of "lie now, check for the type later". Like the following:

type ExtendedFoo = {
  __a?: (...args: unknown[]) => void;
  __b?: (...args: unknown[]) => void;
  __c?: (...args: unknown[]) => void;
} & Foo;

const isExtendedFoo = (input: unknown): input is ExtendedFoo => {
  if (typeof input !== "object" || input === null) {
    return false;
  }
  const candidate = input as ExtendedFoo;
  return (
    typeof candidate.a === "function" &&
    typeof candidate.b === "function" &&
    typeof candidate.c === "function" &&
    (candidate.__a === undefined || typeof candidate.__a === "function") &&
    (candidate.__b === undefined || typeof candidate.__b === "function") &&
    (candidate.__c === undefined || typeof candidate.__c === "function")
  );
};

Unlike in the handbook, the above example has a potential for disaster. In future, one can insert a line between const ... and return ..., where candidate is potentially of the wrong type.

Despite the difference, it's hard to argue as a code review feedback "do not lie to the type system" when the official documentation does exactly that.

I propose to use as for the cases where "we really know" and update the example to the following:

function isFish(pet: Fish | Bird): pet is Fish {
  return 'swim' in pet;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions