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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions