Skip to content

TS ^4 does not recognize array methods on (T[] | T[][]) as callable #40157

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

Closed
dkamyshov opened this issue Aug 20, 2020 · 7 comments
Closed

TS ^4 does not recognize array methods on (T[] | T[][]) as callable #40157

dkamyshov opened this issue Aug 20, 2020 · 7 comments
Assignees
Labels
Breaking Change Would introduce errors in existing code Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@dkamyshov
Copy link

TypeScript Version: 4.0.2, 4.1.0-dev.20200820

Search Terms: union, array methods

Code

The following example uses every method, but the bug affects all other methods as well (map, reduce, etc.).

const a: string[] | string[][] = [];

a.every(b => console.log(b)); // TS-2349: This expression is not callable.

// Attempt №1: suppress TS-7006
a.every((b: string | string[]) => console.log(b)); // TS-2349: This expression is not callable.

// Attempt №2: rewrite as typeguard (as in https://github.com/microsoft/TypeScript/blob/v4.0-beta/lib/lib.es5.d.ts#L1319)
a.every((b: string | string[]): b is string => typeof b === 'string'); // TS-2349: This expression is not callable.

Expected behavior: No errors, code should compile.

Actual behavior: Error TS-2349: This expression is not callable.

Playground Link: https://www.typescriptlang.org/play?ts=4.1.0-dev.20200819#code/MYewdgzgLgBAhgLhtATgSzAcwNoF0YA+yU6WeeMAvDHgNwBQ9cAdAKYBurKAngBQBGVAHwxQkEABtWzCSEwCAlAoZM2nHrwFJUGTIWKkcuBcNHgIk6bPn8lKlhy58tB3fp1ljSQWgiusplDcAA6sIABmMIKUMTAA5B6Yccr0QA

Related Issues: I didn't find anything similar.

@dkamyshov
Copy link
Author

The error disappears if the array is initialized with something inside.

const a: string[] | string[][] = [];

a.every(() => {}); // TS-2349: This expression is not callable.

const b: string[] | string[][] = [[]];

b.every(() => {}); // ok

const c: string[] | string[][] = ['c'];

c.every(() => {}); // ok

const f = (d: string[] | string[][]) => {
    d.every(() => {}); // TS-2349: This expression is not callable.
}

https://www.typescriptlang.org/play?ts=4.1.0-dev.20200819#code/MYewdgzgLgBAhgLhtATgSzAcwNoF0YA+yU6WeeMAvDHgNwBQ9cAdAKYBurKAngBS8BKKgD4YAbwC+AhvVCRYAIySoMOfERVlcFatjy4ZCtpx78hlUZOmM50GMGUlVFDU606aAcmCeDN41x8giLiUjK2sABmVDC8ACaOpGqExEnkuOaW9DA5MHEBpsEWodYS9EA

@dkamyshov
Copy link
Author

dkamyshov commented Aug 20, 2020

Methods, that do not have generic overloads (some, forEach) are not affected.

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Aug 20, 2020
@weswigham
Copy link
Member

This is working as intended (or at least a current design limitation), if a breaking change in 4.0 due to a lib update - #38200 added a second, generic overload to .every, making it so we couldn't resolve a single combined signature for the union (and so it now behaves as .map and friends).

Changing the types you annotate on the argument to .every aren't going to make .every itself callable. The issue is that the union of the two array types contains irreconcilable overload lists for .every (we'd have to create a power set of signatures and resolve them as a new overload list, while also unifying generics in similar positions, and even then it's not necessarily correct) - you need to cast the .every call itself (which is going to break any type guards you try to apply, mind you).

@weswigham weswigham added Breaking Change Would introduce errors in existing code Working as Intended The behavior described is the intended behavior; this is not a bug and removed Needs Investigation This issue needs a team member to investigate its status. labels Aug 20, 2020
@weswigham
Copy link
Member

weswigham commented Aug 20, 2020

@RyanCavanaugh up to you - this break should possibly be noted in the release breaking changes, if we want to keep it (otherwise we'd revert #38200 and unfix its associated issue).

@pleunv
Copy link

pleunv commented Aug 21, 2020

So is there currently any proper workaround? I can't seem to get around the error.

@dkamyshov
Copy link
Author

@pleunv

If introducing runtime typeguard is acceptable (in my case - it is), you could go with something along the lines of:

const isArrayOfArrays = (candidate: unknown[]): candidate is unknown[][] => {
    // you might want to add special case for an empty candidate - this implementation returns `true`
    return candidate.every(Array.isArray);
}

const f = (a: string[] | string[][]) => {
    const g = (b: string | string[]) => {
        // ...
    }

    if(isArrayOfArrays(a)) {
        a.every(g);
    } else {
        a.every(g);
    }
}

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Breaking Change Would introduce errors in existing code Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

5 participants