#TS2345: Argument of type is not assignable to parameter of type

38 messages · Page 1 of 1 (latest)

deft beacon
#

Hello. Earlier this week, I posted the same error as I have now and it was temporarily fixed because I removed generics from this class. I was unable to figure out how to reproduce this error when I originally had it, but now I did and because of that, I will repost it with the playground link.

So I created my own typings for EventEmitter by events to support strict typings of each emitted event. I will post what I created for reference:

export type DefaultEventHolder = { [E: string]: (...args: any[]) => any };
export type EventHolder<E> = { [e in keyof E]: (...args: any[]) => any; };

export declare interface TypedEmitter<Events extends EventHolder<Events> = DefaultEventHolder> {
    addListener<E extends keyof Events>(event: E, listener: Events[E]): this;
    prependListener<E extends keyof Events>(event: E, listener: Events[E]): this;
    prependOnceListener<E extends keyof Events>(event: E, listener: Events[E]): this;
    removeListener<E extends keyof Events>(event: E, listener: Events[E]): this;
    removeAllListeners(event?: keyof Events): this;

    emit<E extends keyof Events>(event: E, ...args: Parameters<Events[E]>): boolean;
    eventNames<E extends keyof Events>(): E[];
    off<E extends keyof Events>(event: E, listener: Events[E]): this;
    on<E extends keyof Events>(event: E, listener: Events[E]): this;
    once<E extends keyof Events>(event: E, listener: Events[E]): this;

    getMaxListeners(): number;
    listenerCount(type: keyof Events): number;
    listeners<E extends keyof Events>(type: E): Events[E][];
    rawListeners<E extends keyof Events>(type: E): Events[E][];
    setMaxListeners(num: number): this;
}

So while attempting to use this custom declaration, I try attaching my own events to this class. In the method TypedEmitter#emit(), the compiler is properly understanding the keys of my event type in the first argument, but ...args, which is typed as Parameters<Events[E]>, errors when calling emit with values that clearly match the parameters of Events[E].

// ProviderEvents
export type ProviderEvents<S extends Structure> = {
    load: (structure: S) => any;
    remove: (structure: S) => any;
};

// EE is a generic that allows you to input more events while combining both 'ProviderEvents' and 'EE'
export class Provider<S extends Structure, E extends EventHolder<E> = {}> extends (EventEmitter as { new<S extends Structure, E extends EventHolder<E> = {}>(): TypedEmitter<ProviderEvents<S> & E> })<S, ProviderEvents<S> & E> implements Required<ProviderOptions<S>>
this.emit("load", structure /* Type: S */);
//         ^ ok     ^ error [but matches Parameters<ProviderEvents<S>["load"]>]

To go more in depth with the error, I attempted to intersect ProviderEvents<S> & EE and also did try a union ProviderEvents<S> | EE, but this only errored on event argument.
TS2345: Argument of type  [S]  is not assignable to parameter of type  Parameters<(ProviderEvents<S> & EE)["load"]> 

toxic orbitBOT
#
n0riega#0

Preview:```ts
import {EventEmitter} from "events"

type DefaultEventHolder = {
[E: string]: (...args: any[]) => any
}
type EventHolder<E> = {
[e in keyof E]: (...args: any[]) => any
}
declare interface TypedEmitter<
Events extends EventHolder<Events> = DefaultEventHolder

{
addListener<E extends keyof Events>(
event: E,
listener
...```

#
n0riega#0

Preview:```ts
import {EventEmitter} from "events"

type DefaultEventHolder = {
[E: string]: (...args: any[]) => any
}
type EventHolder<E> = {
[e in keyof E]: (...args: any[]) => any
}
declare interface TypedEmitter<
Events extends EventHolder<Events> = DefaultEventHolder

{
addListener<E extends keyof Events>(
event: E,
listener
...```

deft beacon
#

2nd playground is updated to continue code consistency with playground and my file

deft beacon
#

Bumping because no one addressed this

green sinew
#

it's hard to engage with this because your repro is so big. i spent some time to reduce it a bit, but have to tend to work things now. if you could try to reduce this more it'd be easier for folks to offer help:

toxic orbitBOT
#
mkantor#0

Preview:```ts
import {EventEmitter} from "events"

type EventHolder<E> = {
[e in keyof E]: (...args: any[]) => any
}
declare interface TypedEmitter<
Events extends EventHolder<Events>

{
emit<E extends keyof Events>(
event: E,
...args: Parameters<Events[E]>
): boolean
...```

deft beacon
#

I kinda did think about how big my playground was earlier but still somehow never shortened it
with that in mind, here is what I got:

toxic orbitBOT
#
n0riega#0

Preview:```ts
import {EventEmitter} from "events"

type EventHolder<E> = {
[e in keyof E]: (...args: any[]) => any
}
declare interface TypedEmitter<
Events extends EventHolder<Events>

{
emit<E extends keyof Events>(
event: E,
...args: Parameters<Events[E]>
): boolean
...```

deft beacon
#

you did awesome at shortening it up with the only flaw being the parameter of test()
its intended to be S generic

green sinew
#

i noticed that too while i was working on further reduction:

toxic orbitBOT
#
mkantor#0

Preview:```ts
type EventHolder<E> = {
[e in keyof E]: (...args: any[]) => any
}
interface TypedEmitter<
Events extends EventHolder<Events>

{
emit<E extends keyof Events>(
event: E,
...args: Parameters<Events[E]>
): boolean
}

type ProviderEvents<S> = {
load: (structure: S) => any
...```

green sinew
#

are the EE extends EventHolder<EE> constraints definitely what you want? those look fishy to me because of how they're circular

#

i'll admit to not really understanding what this code is actually supposed to do, but perhaps EE isn't needed at all? does this make sense?

toxic orbitBOT
#
mkantor#0

Preview:```ts
type EventHolder<E> = {
[e in keyof E]: (...args: any[]) => any
}
interface TypedEmitter<
Events extends EventHolder<Events>

{
emit<E extends keyof Events>(
event: E,
...args: Parameters<Events[E]>
): boolean
}

type ProviderEvents<S> = {
load: (structure: S) => any
remove: (structure: S) => any
}

declare const MyEventEmitter: {
new <S>(): TypedEmitter<ProviderEvents<S>>
}

class Provider<S> extends MyEventEmitter<S> {
test(x: S) {
this.emit("load", x)
}
}```

deft beacon
#

so the code is supposed to inherit EventEmitter

#

but the catch is that I created my own strict typings for EventEmitter

green sinew
#

yeah, i just dropped the dependency there and extracted a minimal version of your custom types for the repro

#

but i think it has the important bits?

deft beacon
#

well I see that you did drop the second generic that was constrained to EventHolder

#

the reason for the constraint was because the compiler ensured the object is typed that way

#

so the compiler knows its any property name along with a callback

green sinew
# toxic orbit

here's the removal of EE applied to your original playground, in case it helps:

toxic orbitBOT
#
mkantor#0

Preview:```ts
import {EventEmitter} from "events"

type DefaultEventHolder = {
[E: string]: (...args: any[]) => any
}
type EventHolder<E> = {
[e in keyof E]: (...args: any[]) => any
}
declare interface TypedEmitter<
Events extends EventHolder<Events> = DefaultEventHolder

{
addListener<E extends keyof Events>(
event: E,
listener
...```

green sinew
deft beacon
#

the EE generic is there if i wanted to add more events

#

so when I inherit Provider, I can use provider events AND EE

#

what im stuck on however is that I inherited Provider, but the original ProviderEvents and my extra events I mentioned with EE generic all work in the inherited class

#

I'll have to attach another playground for demonstration

green sinew
#

yeah i think i'd need a concrete example to get it. probably doesn't help that i don't think i've ever used EventEmitter

deft beacon
#

so the very bottom is an inheritance of Provider and perfectly shows the problems with emit im having

toxic orbitBOT
#
n0riega#0

Preview:```ts
import {EventEmitter} from "events"

type EventHolder<E> = {
[e in keyof E]: (...args: any[]) => any
}
declare interface TypedEmitter<
Events extends EventHolder<Events>

{
emit<E extends keyof Events>(
event: E,
...args: Parameters<Events[E]>
): boolean
...```

green sinew
#

this doesn't yet solve the issue, but i tried to simplify that more while keeping the ad-hoc extension feature:

toxic orbitBOT
#
mkantor#0

Preview:```ts
import {EventEmitter} from "events"

type EventHolder = Record<
PropertyKey,
(...args: any[]) => any

declare interface TypedEmitter<
Events extends EventHolder

{
emit<E extends keyof Events>(
event: E,
...args: Parameters<Events[E]>
): boolean
}

class Structure {}
...```

green sinew
#

the issue might be that EE could be something like { load: (structure: boolean) => any }, in which case you wouldn't be allowed to call this.emit("load", structure), but i'm not entirely sure

deft beacon
#

no other error ive gotten has ever confused me this much