I’m currently implementing authentication with socket.io in TypeScript for my project. When a user connects, I need to store their user_id on socket.user_id after verifying their JWT token in a middleware function.
The Problem:
The default Socket class from socket.io does not include a user_id property. To address this, I created a global.d.ts file in my types folder to extend the Socket interface as follows:
// global.d.ts
import { Socket as DefaultSocket } from 'socket.io';
declare module 'socket.io' {
interface Socket extends DefaultSocket {
user_id: string; // Replace 'any' with your user type if you have one
}
}
Error:
Despite VsCode’s Intellisense recognizing the user_id property correctly, when I run yarn run dev, I encounter the error:
Property 'user_id' does not exist on type 'Socket<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, any>'.
Details:
My app.ts file:
const io = new Server(server, {
cors: {
origin: "*",
credentials: true,
}
});
io.use(async (socket, next) => {
try {
const token = <string>socket.handshake.query.token;
if (!token) return next(new Error("Not Authorized"));
const { user_id }: any = jwt.verify(token, process.env.USER_SESSION_JWT||"");
const user = await User.findOne({ user_id });
if (!user) return next(new Error("Not Authorized"));
socket.user_id = user.user_id;
return next();
} catch (error) {
console.log(error);
}
})
io.on(SocketEvents.CONNECT, socket=>{
socket.join(socket.user_id);
matchHandler(io, socket);
chatHandler(io, socket);
callMatchHandler(io, socket)
})
Here is my package.json
{
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"dev": "nodemon -r tsconfig-paths/register src/app.ts",
"start": "node compiled/server/src/app.js",
"build": "tsc"
},
"dependencies": {
"@types/bcrypt": "^5.0.2",
"@types/body-parser": "^1.19.5",
"@types/cookie-parser": "^1.4.7",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.6",
"@types/multer": "^1.4.11",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cloudinary": "^2.2.0",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.4.4",
"multer": "^1.4.5-lts.1",
"nodemon": "^3.1.4",
"redis": "^4.6.14",
"sharp": "^0.33.4",
"socket.io": "^4.7.5",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.5.3"
}
}
and my tsconfig:
{
"compilerOptions": {
"target": "es5",
"baseUrl": "./src",
"paths": {
"@shared/*": ["../../shared/*"]
},
"module": "commonjs",
"outDir": "compiled",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
Request for Help:
How can I correctly extend the Socket interface in TypeScript so that socket.user_id is recognized without errors during runtime? Any insights or corrections to my approach would be greatly appreciated.