-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unions and intersections of type predicates produce wrong type #17757
Comments
It appears to just grab the first type predicate and ignore the rest. Here is a self-contained repro (2.5.0-dev.20170808) type IsStringOrNumber = ((x: any) => x is number) | ((x: any) => x is string);
declare const x: any;
if ((((x: any) => true) as IsStringOrNumber)(x)) {
x.toFixed();
} Intersecting type predicates seems to have the same result, only the first signature is considered. type IsStringAndNumber = ((x: any) => x is number) & ((x: any) => x is string); |
I knew using "type guards" like that wasn't quite right. 😂 |
Being able to compose these functions would be highly useful. Imagine a rather heterogeneous array elements of elements in a scenario such as import moment from 'moment';
interface Partial {
name?: string;
id?: number;
dob?: moment.Moment
}
declare const partials: Partial[];
type HasName = (x: Partial) => x is {name: string};
type HasDob = (x: Partial) => x is {dob: moment.Moment};
type HasNameAndDob = HasName & HasDob;
const hasNameAndDob: HasNameAndDob = ({name, dob}) => name && dob;
const withNamesAndDobs = mayHaveProps.filter(hasNameAndDob); There is some boilerplate in the example, but throw in a |
I was thinking of this more as a bug report than a feature request, but if we're doing demonstrations of value, this was my scenario. The typescript compiler has lots of I was trying to put together something like this: function isAnyOf<T extends ts.Node>(node: ts.Node, ...preds: Array<(n: ts.Node) => n is T>): node is T {
return preds.some(p => p(node));
}
if (isAnyOf(node, ts.isWhileStatement, ts.isIfStatement)) {
// node should have type ts.WhileStatement | ts.IfStatement
// actually just has type ts.WhileStatement
} But with the current behaviour/bug, |
Probably an easy fix if someone wants to try |
@RyanCavanaugh I'm interested in tackling this issue, can you point me towards what changes will be needed? I was able to find (and fix) an issue where Type Predicates weren't being correctly handled by |
@charlespierce By coincidence I made a commit just now fixing the union half in #17600. You could look at the intersection part once that's in; it's marked with |
@andy-ms Thanks, I'll take a look at the intersection part. I actually had just figured out what I was missing, but I'm glad to see I came up with essentially the same solution as you for the union signatures. |
I think this issue can be closed. The union case correctly works by selecting both predicate types: type IsStringOrNumber = ((x: any) => x is number) | ((x: any) => x is string);
declare const x: any;
if ((((x: any) => true) as IsStringOrNumber)(x)) {
x.toFixed(); // x has type number | string
} The intersection case still selects the first overload, but this is a general problem with intersections of signatures---it is not a particular issue with type predicates. |
TypeScript Version: 2.5.0-dev.20170803
Code
Expected behavior:
Resulting type should be
(node: ts.Node) => node is (ts.DoStatement | ts.WhileStatement)
Actual behavior:
Resulting type is
(node: ts.Node) => node is ts.DoStatement
It seems to just take the first one.
The text was updated successfully, but these errors were encountered: