Skip to content
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

Improved union/intersection type inference #5738

Merged
merged 4 commits into from
Nov 24, 2015

Conversation

ahejlsberg
Copy link
Member

This PR improves type inference involving source and target sides that are both union or intersection types. When inferring from a type S to a type T, if S and T are both union types or both intersection types, we first reduce S and T by removing constituents that are matched by a constituent in the other type. For example, when inferring from string | string[] to string | T, we reduce the types to string[] and T, thus inferring string[] for T.

An example:

type Maybe<T> = T | void;

function isDefined<T>(x: Maybe<T>): x is T {
    return x !== undefined && x !== null;
}

function isUndefined<T>(x: Maybe<T>): x is void {
    return x === undefined || x === null;
}

function getOrElse<T>(x: Maybe<T>, defaultValue: T): T {
    return isDefined(x) ? x : defaultValue;
}

function test1(x: Maybe<string>) {
    let x1 = getOrElse(x, "Undefined");         // string
    let x2 = isDefined(x) ? x : "Undefined";    // string
    let x3 = isUndefined(x) ? "Undefined" : x;  // string
}

function test2(x: Maybe<number>) {
    let x1 = getOrElse(x, -1);         // number
    let x2 = isDefined(x) ? x : -1;    // number
    let x3 = isUndefined(x) ? -1 : x;  // number
}

Fixes #2264.
Fixes #4212.
Fixes #5417.
Fixes #5456.

@@ -6244,6 +6254,41 @@ namespace ts {
}
}

function typeIdenticalToSomeType(source: Type, target: UnionOrIntersectionType): boolean {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

return forEach(target.types, t => isTypeIdenticalTo(source, t) || undefined);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely an option, but not quite as efficient. I will keep what's there.

@sandersn
Copy link
Member

👍

}
}
if (modified) {
return source.flags & TypeFlags.Union ? getUnionType(sourceTypes, /*noSubtypeReduction*/ true) : getIntersectionType(sourceTypes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you break this into multiple lines?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use wordwrapping man.

@mariusschulz
Copy link
Contributor

@ahejlsberg You've got a little typo in your code example: isUndefined<T> should use or rather than and, shouldn't it? Just trying to avoid confusion here.

function isUndefined<T>(x: Maybe<T>): x is void {
    return x === undefined || x === null;
}
@ahejlsberg
Copy link
Member Author

@mariusschulz Yes indeed, thanks for catching that.

@ENikS
Copy link

ENikS commented Dec 7, 2015

Will this address issue mentioned in #3215 ?

@ahejlsberg
Copy link
Member Author

@ENikS No, the two are unrelated.

@masaeedu
Copy link
Contributor

masaeedu commented Feb 9, 2016

@ahejlsberg Would inferring T more accurately in the following scenario be worthwhile:

interface A { a: any; }

function test<T>(arg: A & T): T { ... }

let result = test({a: "", b: 10});

The type of result is inferred as {}, although I would expect it to be inferred as {b: number}. Admittedly I don't have a very clear use case for this at the moment.

@ahejlsberg
Copy link
Member Author

@masaeedu We could potentially explore that, but it could get complex and would require type inference to manufacture new types, neither of which I'm crazy about. Specifically, in your example, we'd have to manufacture a new type { b: number } and infer that for T. I think it is desirable to have the property that type inference never infers types that don't already occur in your code.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
8 participants