microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.81k stars 12.46k forks source link

Type predicate assertion does not carry over when used in another function #51326

Closed yogiduzit closed 1 year ago

yogiduzit commented 1 year ago

Bug Report

πŸ”Ž Search Terms

πŸ•— Version & Regression Information

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

interface Animal {
  name: string;
}

interface Bird extends Animal {
  wingSpan: string;
}

/**
 * Asserts that an animal is of type T if condition is true for that animal.
 */
function isAnimalOfType<T extends Animal>(animal: Animal, condition: (animal: T) => boolean): animal is T {
  return condition(animal as T);
}

function isBird(animal: Animal) {
  return isAnimalOfType<Bird>(animal, (bird) => Boolean(bird.wingSpan));
}

function getWingspan(bird: Bird) {
  return bird.wingSpan;
}

const animals = [{ name: "a" }, { name: "a", wings: "3ft" }] as Animal[];

function checkAnimals(animals: Animal[]) {
  return animals.map(animal => {
    console.log(animal);
    if (isBird(animal)) {
      console.log(getWingspan(animal));
    }
  })
}

πŸ™ Actual behavior

The call to getWingspan shows an error saying Argument of type 'Animal' is not assignable to parameter of type 'Bird'.

πŸ™‚ Expected behavior

The function isAnimalOfType already asserts that the Animal will be of type T. So, why does isBird not automatically assert that?

fatcerberus commented 1 year ago

Type predicates are not automatically inferred. You need to explicitly declare isBird as returning animal is Bird.

jcalz commented 1 year ago

Duplicate of #10734

typescript-bot commented 1 year ago

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

danvk commented 5 months ago

This was marked as a duplicate of #10734, which was closed by #57465. But that PR only let "identifier" type predicates flow, not assertion predicates. The example in this issue is still broken.

This specific issue would be fixed by #58495, see playground.