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

Issues with io.on() typings #3833

Closed
westy92 opened this issue Mar 11, 2021 · 3 comments · Fixed by #3834
Closed

Issues with io.on() typings #3833

westy92 opened this issue Mar 11, 2021 · 3 comments · Fixed by #3834
Labels
bug Something isn't working

Comments

@westy92
Copy link
Contributor

westy92 commented Mar 11, 2021

Describe the bug
After upgrading to socket.io 4.0.0, my code no longer compiles when using string enums for event names.

To Reproduce

Please fill the following code example:

Socket.IO server version: 4.0.0
[email protected]

Server

import { Server } from "socket.io";

const io = new Server(3000, {});

// this works fine
io.on("connection", (socket) => {});

enum Event {
    CONNECTION = "connection",
}
// Fails to compile!
// Argument of type '(socket:Socket) => void' is not assignable to parameter of type 'never'.
// ts(2345)
io.on(Event.CONNECTION, (socket) => {});

Client
N/A

Expected behavior
I expect my code to compile like it does for version 2.x and 3.x.

Platform:
N/A

@westy92 westy92 added the bug Something isn't working label Mar 11, 2021
@MaximeKjaer
Copy link
Contributor

MaximeKjaer commented Mar 12, 2021

I'm able to replicate this, and I think I've found the root cause of the error. We use TypeScript conditional types internally to determine whether to get the listener type from a reserved events map, or from a user-provided events map. However, it seems that conditional types have some very weird behavior around enums. I've been able to minimize the issue to the following:

enum Events {
  CONNECTION = "connection",
  TEST = "test",
}
type Names = "test" | "connection";
type NameOrNull<N> = N extends Names ? N : null;

// Sanity checks:
type test1 = NameOrNull<"test">; // returns type `"test"`
type test2 = NameOrNull<"not-a-name">; // returns type `null`
type test3 = Events.TEST extends Names ? Events.TEST : null // returns type `Events.TEST`

// Weird behavior:
type test4 = NameOrNull<Events.TEST>; // returns type `never` (???)

I would expect test3 to be of type Events.TEST, but it comes out as type never, which is quite surprising. This could be a bug in the compiler. I'll try to do a bit more research into this, and if nothing comes up, I'll open an issue on the compiler.

Until we find a solution to this, there are two possible ways of dealing with this problem:

  • Cast the enum member as Event.CONNECTION as "connection" to get TypeScript to treat it as a string literal type instead of an enum
  • Migrate to strictly typed events by refactoring the enum to an interface that maps event names to event listener types, and use string literals for the event names

I realize that these are unfortunately less than ideal solutions ☹️ I'll try to find a better fix for this!

@MaximeKjaer
Copy link
Contributor

This seems to be linked to microsoft/TypeScript#41778. I'll add the above bug reproduction to that issue.

In the meantime, I've submitted a workaround in #3834.

@ZachHaber
Copy link
Contributor

It appears that this issue is fixed in Typescript 4.9 without the fallback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants