Different return type inference when union with common members is reduced
Closed this issue · 1 comments
Steps to reproduce
function validate() {
if(Math.random() > 0.5) {
return utilValidate();
}
return { valid: true };
};
declare function utilValidate(): {
valid: boolean;
msg?: undefined;
} | {
valid: boolean;
msg: string;
}
validate().msg; // Error in TSGO
Behavior with typescript@5.8
The return type of validate is { valid: boolean; msg?: undefined; } and validate().msg has no error
Behavior with tsgo
validate().msg raises the error: Property 'msg' does not exist on type '{ valid: boolean; }'. suggesting the return type of validate is { valid: boolean; }. If utilValidate is defined before validate there is no error
Maybe related to #1738
This is a type ordering issue. The type { valid: boolean; msg?: undefined } and the fresh object literal type { valid: boolean } are both strict subtypes of each other, so subtype reduction will keep whichever one is ordered first in a union type. You can force the error with typescript@5.8 by reversing the branches of the if statement:
function validate() {
if(Math.random() <= 0.5) {
return { valid: true };
}
return utilValidate();
};
declare function utilValidate(): {
valid: boolean;
msg?: undefined;
}
validate().msg; // Error in typescript@5.8Likewise, you can make the error go away in tsgo by moving the declaration of utilValidate before the declaration of validate.
I'll give some thought to whether we can make the strict subtype relation pick a winner here. Ideally the type with the explicitly declared undefined property should be considered a strict supertype of the fresh type without (because the fresh object literal type effectively has an infinite set of properties of type undefined), but it may not be feasible.