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

better type coercion : instead of cannot invoke an expression whose type lacks a call signature #20877

Closed
SalathielGenese opened this issue Dec 23, 2017 · 6 comments
Labels
Duplicate An existing issue was already created

Comments

@SalathielGenese
Copy link

SalathielGenese commented Dec 23, 2017

TypeScript Version: 2.6.2

{
    "compilerOptions": {
        "strict": true,

Code

export interface HopeLike<T, S = void>
{
    <U>(callabale: (value: T) => U, asHoped?: (value: U) => boolean): HopeLike<U, T>;
    or<U>(callable: (parent: S, value: T) => U): this | HopeLike<U, T>;
    get(): T | void;
}

let h: HopeLike<number> = <any>void 0;
h(() => 1).or(() => '').or(p => ({})); // here is the issue, from the `.or(...`

Note that this code works well if .or call is not called twice!

A concrete usecase

const EVENT: Event<typeof source> = new Event(event, source);
const HANDLERS = hope(EVENT_EMMITTERS.get(this)!)
    (container => container.get(event)!)
// Since it is not sure this key is mapped, let's ensure, just in case.
// This' achieved by calling `.or(callback)` which will run its callback only if the `event` key is mapped
    .or(container => container.set(event, []).get(event)!)
        .get(); // were finally fetch the real result, which may be `void` as well
if (HANDLERS) for (let handler of HANDLERS)
    if (void handler.call(EVENT, EVENT, ...data) || EVENT.isPropagationStopped)
        break;

Where :

  1. const EVENT_EMMITTERS: WeakMap<EventEmitter<any>, Map<string, EventHandlerLike<any>[]>> = new WeakMap();
  2. The this context is instance of EventEmitter

Expected behavior:
I had expected these to be inferred after the.or second call:

  1. parameter p as number | string
  2. the return value to be: HopeLike<number, number> | HopeLike<U, number> | HopeLike<string, number> | HopeLike<U, string>

Actual behavior:
The error says cannot invoke an expression whose type lacks a call signature. Type '(<U>(callable: (parent: number, value: number) => U): HopeLike<number, number> | HopeLike<U, nu...'

@DanielRosenwasser
Copy link
Member

This might be fixed by #17819; @weswigham?

@SalathielGenese
Copy link
Author

SalathielGenese commented Dec 24, 2017

Thanks @DanielRosenwasser for your interest. As far as I can tell of your example, the union type is for signatures with same return type and but differing parameter types.

In my case, the parameter is of one type only. The return type changes depending of what happen at rutime: the callback in the .or(...) call is executed only if the computed value from previous HopeLike<T, S>(...) call were not satisfied (according to the predicate asHoped(...)).

So, no sure this might be a fix.


However, I was able to increase typing complexity, wrote a solution that worked, then striped off unnecessary code until I got a simple solution that works just as I expected (even the implementation is now tiny).

export interface HopeLike<T, S = void>
{
    <U>(map: (value: T) => U, asHoped?: (value: U) => boolean): HopeLike<U, T>;
    or(map: (parent: S) => T): HopeLike<T, S>;
    get(): T | void;
}

You may discover that the redesigned interface express something strongly different from the first version. I figured out that I wrongly implemented the solution to that my specific problem. Nonetheless, the question remains: [W]hy TypeScript cannot (yet) infer that union type of two functions is a function, thus callable?

Therefore, I call for possible duplicates as I believe this isn't the first issue opened about...

@sylvanaar
Copy link

sylvanaar commented Dec 26, 2017

I have reduced the problem down so it is easier to to see the issue:

const callme = {} as (<T>() => T) | (() => number);

callme(); // error "cannot invoke an expression whose type lacks a call signature."

@SalathielGenese
Copy link
Author

You got it clear, @sylvanaar

@RyanCavanaugh
Copy link
Member

Duplicate of #7294

@RyanCavanaugh RyanCavanaugh marked this as a duplicate of #7294 Dec 27, 2017
@mhegazy mhegazy added the Duplicate An existing issue was already created label Jan 9, 2018
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

6 participants