I'm starting a small project that uses TypeScript, Next.js, and Socket.io. I don't understand how to tell TypeScript that I am kind of "merging" these two. For example, here are my files.
/api/socket.ts:
import { NextApiRequest, NextApiResponse } from 'next'
import { Server } from 'socket.io'
const SocketHandler = (req: NextApiRequest, res: NextApiResponse) => {
if (res.socket.server.io) {
console.log('Socket is already running.')
} else {
console.log('Socket is initializing...')
const io = new Server(res.socket.server)
res.socket.server.io = io
io.on('connection', (socket) => {
socket.on('input-change', (msg) => {
socket.broadcast.emit('update-input', msg)
})
})
}
res.end()
}
export default SocketHandler
/components/client.tsx:
import { useEffect, useState } from 'react'
import io from 'socket.io-client'
import type { ChangeEvent } from 'react'
import type { Socket } from 'socket.io-client'
let socket: undefined | Socket
export default function Client() {
const [input, setInput] = useState('')
useEffect(() => {
socketInitializer()
}, [])
const socketInitializer = async () => {
fetch('/api/socket')
socket = io()
socket.on('connect', () => {
console.log('connected')
})
socket.on('update-input', (msg) => {
setInput(msg)
})
}
const onChangeHandler = (e: ChangeEvent<HTMLInputElement>) => {
setInput(e.target.value)
if (socket !== undefined) {
socket.emit('input-change', e.target.value)
}
}
return (
<input
placeholder="Type something"
value={input}
onChange={onChangeHandler}
/>
)
}
And this code is working. But I'm getting all kinds of warnings / errors, like "Property 'server' does not exist on type 'Socket'", "Object is possibly 'null'." (on res itself).
I understand the main issue is TypeScript does not know I am adding .io on the res.socket.server object. But what I don't understand is A) how to tell it that I am adding that .io, B) why res and socket are possibly null, and C) why .server does not exist on res.socket, according to TypeScript.
I just need some direction and possibly higher-level explanation of how to tackle this. I think I need a .d.ts file, or maybe I just a new interface, but I am not really sure how to properly write a new interface without over-riding types that are already in place.
I found an answer. I don't know if it is the correct way of going about it, but this is what I did.
First I made new TypeScript interfaces, starting with the .io property, and moved up all the way back to res. All of these extend what NextApiResponse already is, and just add new properties.
import type { Server as HTTPServer } from 'http'
import type { NextApiRequest, NextApiResponse } from 'next'
import type { Socket as NetSocket } from 'net'
import type { Server as IOServer } from 'socket.io'
interface SocketServer extends HTTPServer {
io?: IOServer | undefined
}
interface SocketWithIO extends NetSocket {
server: SocketServer
}
interface NextApiResponseWithSocket extends NextApiResponse {
socket: SocketWithIO
}
Then I just assigned my new NextApiResponseWithSocket to the res parameter.
const SocketHandler = (_: NextApiRequest, res: NextApiResponseWithSocket) => { ... }
If there is a better way to do this, I would love to know.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With