Do you have any examples off the top of your head?
Do you have any examples off the top of your head?
It yields no warnings or errors at compile stage but gives runtime error based on a wrong flow-based type inference. The crux of it is that something can be a Bird (with "fly" function) but can also have any other members, like "swim" because of structural typing (flying is the minimum expected of a Bird). The presence of a spurious "swim" member in the bird causes tsc to infer in a conditional that checks for a "swim" member that the animal must be a Fish or Human, when it is not (it's just a Bird with an unrelated, non-function "swim" member).
type Fish = { swim: () => void };
type Bird = { fly: () => void };
type Human = { swim?: () => void; fly?: () => void };
function move(animal: Fish | Bird | Human) {
if ("swim" in animal) {
// TSC infers wrongly here the presence of "swim" implies animal must be a Fish or Human
onlyForFishAndHumans(animal);
} else {
animal;
}
}
function onlyForFishAndHumans(animal: Fish | Human) {
if (animal.swim) {
animal.swim(); // Error: attempt to call "not-callable".
}
// (receives bird which is not a Fish or Human)
}
const someObj = { fly: () => {}, swim: "not-callable" };
const bird: Bird = someObj;
move(bird);
// runtime error: [ERR]: animal.swim is not a function
[1] https://www.typescriptlang.org/docs/handbook/2/narrowing.htm... if (typeof animal.swim === 'function') {....} type Bird = { id: number };
type Human = { swim?: () => void; id: number };
function move(animal: Bird | Human) {
onlyForHumans(animal);
}
function onlyForHumans(swimmer: Human) {
if (swimmer.swim) {
swimmer.swim();
}
}
const someObj = { id: 1, swim: "not-callable" };
move(someObj);
`someObj` gets casted as `Bird`, then `animal` gets casted as `Human`. Unfortunately unions here are indeed unsound.As as workaround you could add a property `type` (either `"bird"` or `"human"`) to both `Bird` and `Human`, then TypeScript would be able to catch it.
For TypeScript, they list an example with `instanceof`:
https://counterexamples.org/polymorphic-union-refinement.htm...
In the playground:
https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABA...
const foo: string[] = ['foo']
// inferred as string
// though it is undefined
const bar = foo[1]