Skip to content

Overload resolution: Choosing the wrong overload when arguments involve generic wrappers #13570

Closed
@krryan

Description

@krryan

TypeScript Version: 2.2.0-dev.20161221

Code

function filter<P extends Q[], Q, R extends Q>(values: P, isR: (value: Q) => value is R): R[];
function filter<R>(values: R[], isValid: (value: R) => boolean): R[];
function filter<P extends Q[], Q, R extends Q>(values: P, isR: (value: Q) => value is R): R[] {
    const result: R[] = [];
    values.forEach(q => {
        if (isR(q)) {
            result.push(q);
        }
    });
    return result;
}

interface Foo<T> {
    foo: T;
}
interface Bar<T> {
    bar: T;
}
type FooBar<T> = Foo<T> | Bar<T>;

const foobars: FooBar<{}>[] = [];

function isFoo<T>(foobar: FooBar<T>): foobar is Foo<T> {
    return 'foo' in foobar;
}
filter(foobars, isFoo).map(foo => foo.foo);

filter<FooBar<{}>[], FooBar<{}>, Foo<{}>>(foobars, isFoo).map(foo => foo.foo);

function isFooObject(foobar: FooBar<{}>): foobar is Foo<{}> {
    return isFoo(foobar);
}
filter(foobars, isFooObject).map(foo => foo.foo);

(also on the Playground)

Expected behavior:
Compiles without error.

Actual behavior:
The first invocation of filter, filter(foobars, isFoo).map(foo => foo.foo);, has an error Property 'foo' does not exist on type 'FooBar<{}>'. The second/less-specific overload of filter is being selected, even though (as the second invocation shows) the first/more-specific overload is valid, and also despite the fact (as shown by the third invocation) the correct overload is chosen when the inferred type does not involve a generic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions