-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Closed as duplicate of#15048
Description
π Search Terms
predicate narrowing
β Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
When I have a conditional that narrows a type in the TRUE branch but does not narrow in the FALSE branch, I have no way to extract the condition into a function and preserve the narrowing. That is, I have an implication of the form a => b without !a => !b. Existing type predicates require a <=> b.
The existing type predicate functionality doesn't help me because the compiler assumes they can be inverted. I have no way to narrow the type only if my predicate returns true, but do no narrowing when it returns false.
π Motivating Example
function interestingDifferenceInline(x?: number, y?: number) {
if (x !== undefined && x > 12 && y !== undefined && y > 12) {
// x and y correctly have type number here, undefined narrowed away.
// however, my logic is duplicated.
return x - y;
} else {
// x and y are not narrowed here, still have type undefined | number.
return undefined;
}
}
// I should be able to extract the duplicated logic into a function
// and get the same type narrowing
function interestingDifferenceExtracted(x?: number, y?: number) {
if (isInteresting(x) && isInteresting(y)) {
// TYPE ERROR: x and y should have type number here, but have number | undefined.
return x - y;
} else {
return undefined;
}
}
function isInteresting(x: number | undefined): boolean {
// if this function returns true, x should be narrowed to number,
// if this function returns false, it should not be narrowed at all.
// We have implication in one direction, but not its inverse.
return x !== undefined && x > 12;
}
// this is wrong:
function isInterestingIncorrectPredicate(x?: number): x is number {
// this correctly narrows to number if we do if(isInterestingIncorrectPredicate(x)),
// but incorrectly narrows to undefined if we do if(!isInterestingIncorrectPredicate(x))
// type predicates are 'necessary and sufficient', but our condition is
// 'sufficient but not necessary'. That is, we don't know that x is NOT a
// number if this predicate returns false.
return x !== undefined && x > 12;
}
// what we want is some kind of inverse predicate, something like:
// returning true gives us type information, returning false does not.
// function isInterestingDesired(x?: number): x is not undefined {
// if this function returns true x is narrowed to number, excluding undefined.
// if this function returns false nothing is narrowed.
// }π» Use Cases
I want to improve my code quality by extracting complex, duplicated code into a function without losing type information.
Metadata
Metadata
Assignees
Labels
No labels