jza
jza

Reputation: 33

Merging TypeScript interface declaration for Socket.IO is not working

I have been going around in circles looking for how to add a property to an interface for Socket.IO v3. So naturally I have looked in the TypeScript documentation at module augmentation and how to write declaration files but I can't get it working, and I don't know what I am doing wrong. So what's happening is that VSCode is complaining that the session property does not exist on the Handshake interface.

This is what I am trying to accomplish:

// src/types/socket.io/index.d.ts
import { Session, SessionData } from "express-session";
import { EntityContext } from "../../contexts/EntityContext";

declare module "socket.io" {
  interface Handshake {
    session?: Session & Partial<SessionData>;
    sessionID?: string;
    context?: EntityContext;
  }
}

Project Structure

- package.json
- tsconfig.json
- /src
    - /types
        - /socket.io
            - index.d.ts
- index.ts
// tsconfig.json
{
   "compilerOptions": {
      "lib": [
         "es5",
         "es6"
      ],
      "target": "es5",
      "module": "commonjs",
      "moduleResolution": "node",
      "outDir": "./build",
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "sourceMap": true,
      "allowSyntheticDefaultImports": true,
      "baseUrl": ".",
      "typeRoots": [
         "node_modules/@types",
         "src/types"
      ],
      "paths": {
         "socket.io": ["node_modules/socket.io/dist", "src/types/socket.io"]
      }
   },
   "files": [
      "src/index.ts",
      "src/types/socket.io/index.d.ts"
   ]
}
// package.json
{
 ...
 "dependencies": {
    "apollo-server-express": "^2.21.1",
    "argon2": "^0.27.1",
    "connect-redis": "^5.1.0",
    "cors": "^2.8.5",
    "dataloader": "^2.0.0",
    "express": "^4.17.1",
    "express-session": "^1.17.1",
    "graphql": "^15.5.0",
    "pg": "^8.5.1",
    "redis": "^3.0.2",
    "reflect-metadata": "^0.1.10",
    "socket.io": "^3.1.2",
    "type-graphql": "^1.1.1",
    "typeorm": "0.2.31",
    "express-socket.io-session": "^1.3.5"
  },
  "devDependencies": {
    "@types/component-emitter": "^1.2.10",
    "@types/connect-redis": "0.0.16",
    "@types/cookie": "^0.4.0",
    "@types/cors": "^2.8.10",
    "@types/express": "^4.17.11",
    "@types/express-session": "^1.17.3",
    "@types/node": "^8.0.29",
    "@types/redis": "^2.8.28",
    "ts-node": "3.3.0",
    "typescript": "3.3.3333"
  }
}

I don't know if you can tell but I have pretty much grabbed anything and everything I've found to try and solve this but to no avail. I just don't understand how the declarations should work..

If anyone can spot any errors I have made or if you can point me to a guide that can solve this for TypeScript 3 and VSCode I would really appreciate it.

Let me know if you need more information and I will provide it.

Upvotes: 3

Views: 869

Answers (1)

Mirco S.
Mirco S.

Reputation: 2640

Your module declaration is called 'socket.io', because you mapped the types in the tsconfig, but this is not necessary. You also mapped the type file itself "socket.io": ["node_modules/socket.io/dist", "src/types/socket.io"] which probably breaks the augmentation. Also node_modules/socket.io/dist is the wrong path. Correct would be node_modules/socket.io/dist/socket. I would recommend to remove the complete path configuration, since it will just make things harder.

Without the path configuration you have to tell typescript exactly which module you want to augment as shown in the examples of the documentation. When you import the unaugmented Handshake interface normally it comes from 'socket.io/dist/socket'. So the path you would use for a normal import, like import { Handshake } from 'socket.io/dist/socket';, is also the name for the declared module. You also have to add an import for the original Handshake interface, if you want to merge the declarations and not replace it. The *.d.ts file should look like this:

import { Session, SessionData } from "express-session";
import { EntityContext } from "../../contexts/EntityContext";
// You need to import the original interface to extend it, if you don't,
// you will instead replace the orginal Handshake interface with your own definition. 
import { Handshake } from 'socket.io/dist/socket';

declare module 'socket.io/dist/socket' {
    interface Handshake {
        session?: Session & Partial<SessionData>;
        sessionID?: string;
        context?: EntityContext;
    }
}

Upvotes: 1

Related Questions