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

Potential regression in 4.2 - Calling .map on T[] | Y[] is now callable but leads to an implicit any error #42646

Closed
tom-sherman opened this issue Feb 4, 2021 · 12 comments
Labels
Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript
Milestone

Comments

@tom-sherman
Copy link

Bug Report

πŸ”Ž Search Terms

expression not callable union arrays

πŸ•— Version & Regression Information

This changed between versions 4.1.3 and 4.2 beta

⏯ Playground Link

https://www.typescriptlang.org/play?ts=4.1.3#code/CYUwxgNghgTiAEYD2A7AzgF3gDwFzxQFcBbAIxBgG0BdeAH3kxgEsUBzGgKE+wDpioABwAUAM3gBeAHzxRASiA

https://www.typescriptlang.org/play?ts=4.2.0-beta#code/CYUwxgNghgTiAEYD2A7AzgF3gDwFzxQFcBbAIxBgG0BdeAH3kxgEsUBzGgKE+wDpioABwAUAM3gBeAHzxRASiA

πŸ’» Code

declare const x: number[] | string[]

x.map(f => f)

πŸ™ Actual behavior

.map is now callable but f as an implicit type of any, leading to an implicit any error.

πŸ™‚ Expected behavior

The current behaviour in 4.1.3 is to mark the map() call as not callable as well as f should have an implicit any type.

I don't know if this is a regression but I'm raising it because this change in behaviour is not documented in the release notes.

@tom-sherman tom-sherman changed the title Potential regression in 4.2 - Calling .map on T[] | Y[] is now callback but leads to an implicit any error Potential regression in 4.2 - Calling .map on T[] | Y[] is now callable but leads to an implicit any error Feb 4, 2021
@weswigham
Copy link
Member

It's not a regression (definitionally, going from can't call at all to implicitly any is a less restrictive error - now you can explicitly annotate it and actually resolve the call), and we already have #42620 open to improve the experience further~

@thorn0
Copy link

thorn0 commented Feb 4, 2021

But why does it allow specifically map (and some)? every and filter are still errors.

related issue: #35045

@weswigham
Copy link
Member

every and filter have multiple overloads (with differing numbers of type parameters) which makes the union call difficult to resolve, while map only has one overload, so combining the signatures is much more straightforward.

@thorn0
Copy link

thorn0 commented Feb 4, 2021

Is there any chance for every to work for unions of read-only array types in the foreseeable future?

@weswigham
Copy link
Member

Not in the immediate future, no. The readonly-ness isn't what matters for our ability to check it - the overload count/genericness of the signature is. We've been slowly expanding how much of that we can check, but I can't make any claims as to how far we'll go or by when.

@thorn0
Copy link

thorn0 commented Feb 4, 2021

I see, thanks. For some reason, I thought these methods are disallowed because of type safety. E.g., what can be pushed to an array if it's type is string[] | number[]? So it looked like readonly-ness might help. But now I see I misunderstood the problem.

@thorn0
Copy link

thorn0 commented Feb 4, 2021

Anyway, wouldn't it be a helpful trick to implicitly transform readonly string[] | readonly number[] to readonly (string | number)[] for the purposes of signature merging?

@tom-sherman
Copy link
Author

[...] and we already have #42620 open to improve the experience further~

@weswigham It's not clear to me how your PR relates to this issue, do you have a playground link I can poke around with?

@thorn0
Copy link

thorn0 commented Feb 4, 2021

@tom-sherman Previously, such calls were disallowed because merging signatures for generic overloaded functions is a tricky procedure, and it wasn't implemented. Now, in some cases, it became possible. After merging that PR, it'll be possible in more cases. Hopefully, we'll read more on that in the release blog post.

@RyanCavanaugh RyanCavanaugh added Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript labels Feb 5, 2021
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Feb 5, 2021
@thorn0
Copy link

thorn0 commented Aug 26, 2022

Now that it works for map, I wonder if this issue should be renamed with a mention of filter and every in the title, That'd make it more discoverable.

@thorn0
Copy link

thorn0 commented Aug 26, 2022

An isolated real-world case to demo how this issue is a pain:
playground

import * as Babel from "@babel/types";
import { TSESTree } from "@typescript-eslint/typescript-estree";
type Node = Babel.Node | TSESTree.Node;

declare const node: Node;

if (
    (node.type === "GenericTypeAnnotation" || node.type === "TSTypeReference") &&
    (!node.typeParameters ||
        node.typeParameters.params.length <= 2 && node.typeParameters.params.every(n => true))
) { }

every causes the following error:

This expression is not callable.
  Each member of the union type '{ <S extends TSType>(predicate: (value: TSType, index: number, array: TSType[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: TSType, index: number, array: TSType[]) => unknown, thisArg?: any): boolean; <S extends TSType>(predicate: (value: TSType, index: number, array: TSType[]) => value is S, thi...' has signatures, but none of those signatures are compatible with each other.(2349)

@jakebailey
Copy link
Member

Closing as fixed in #53489; reading the PR, it has this case as a test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants