N
Nuxt4d ago
Toffe

Websocket "unknown" type in typescript app

I have this plugin, and I want to use the send functionallity on components. I also want to add others later on (subscribe to topic etc).
import { defineNuxtPlugin } from "#app";

export default defineNuxtPlugin(() => {
const ws = new WebSocket("ws://localhost:3000/websocket");

ws.onopen = () => console.log("Connected to WS");
ws.onmessage = (message) => {
const parsedData = JSON.parse(message.data);
console.log("Received data:", parsedData);
};

ws.onerror = (error) => console.log(`Error: ${error}`);
ws.onclose = () => console.log("Disconnected from WS");

function send(data: string | object) {
ws.send(typeof data === "string" ? data : JSON.stringify(data));
}

return {
provide: {
$ws: { send }
}
};
});
import { defineNuxtPlugin } from "#app";

export default defineNuxtPlugin(() => {
const ws = new WebSocket("ws://localhost:3000/websocket");

ws.onopen = () => console.log("Connected to WS");
ws.onmessage = (message) => {
const parsedData = JSON.parse(message.data);
console.log("Received data:", parsedData);
};

ws.onerror = (error) => console.log(`Error: ${error}`);
ws.onclose = () => console.log("Disconnected from WS");

function send(data: string | object) {
ws.send(typeof data === "string" ? data : JSON.stringify(data));
}

return {
provide: {
$ws: { send }
}
};
});
But when using it it says it is unknown type? Is the type not transferred using nuxtplugins? See image for example.
No description
8 Replies
kapa.ai
kapa.ai4d ago
Hi @Toffe. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- You're experiencing a TypeScript issue with your WebSocket plugin in Nuxt. This is a known issue, particularly with certain IDEs. The Problem When you create a plugin that provides helpers (like your WebSocket functionality), TypeScript should automatically type these helpers. However, in your case, the $ws object is being typed as unknown, which prevents TypeScript from recognizing the send method. Solution: Type Your Plugin To fix this issue, you need to properly type your plugin by augmenting the TypeScript declarations. Based on the knowledge sources, you should add type declarations for your WebSocket plugin:
// Create a types/index.d.ts file (or similar)
declare module '#app' {
interface NuxtApp {
$ws: {
send: (data: string | object) => void
}
}
}

declare module 'vue' {
interface ComponentCustomProperties {
$ws: {
send: (data: string | object) => void
}
}
}

export {}
// Create a types/index.d.ts file (or similar)
declare module '#app' {
interface NuxtApp {
$ws: {
send: (data: string | object) => void
}
}
}

declare module 'vue' {
interface ComponentCustomProperties {
$ws: {
send: (data: string | object) => void
}
}
}

export {}
This will properly type your $ws helper so TypeScript recognizes it when you use useNuxtApp() or access it in your components. Known IDE Issue It's worth noting that there's a known bug specifically in JetBrains IDEs (PhpStorm, WebStorm) related to this issue. According to a Stack Overflow post, the same code works fine in VS Code but shows type errors in JetBrains products: Stack Overflow Post The bug is tracked here: https://youtrack.jetbrains.com/issue/WEB-59818/Vue-custom-global-properties-added-by-augmenting-vue-are-not-resolved Alternative Approach If you prefer a more modern approach, consider creating a composable instead of a plugin. This can provide better type safety and is the recommended approach in Nuxt 3:
// composables/useWebSocket.ts
export function useWebSocket() {
const ws = new WebSocket("ws://localhost:3000/websocket");

ws.onopen = () => console.log("Connected to WS");
ws.onmessage = (message) => {
const parsedData = JSON.parse(message.data);
console.log("Received data:", parsedData);
};

ws.onerror = (error) => console.log(`Error: ${error}`);
ws.onclose = () => console.log("Disconnected from WS");

function send(data: string | object) {
ws.send(typeof data === "string" ? data : JSON.stringify(data));
}

return { send };
}
// composables/useWebSocket.ts
export function useWebSocket() {
const ws = new WebSocket("ws://localhost:3000/websocket");

ws.onopen = () => console.log("Connected to WS");
ws.onmessage = (message) => {
const parsedData = JSON.parse(message.data);
console.log("Received data:", parsedData);
};

ws.onerror = (error) => console.log(`Error: ${error}`);
ws.onclose = () => console.log("Disconnected from WS");

function send(data: string | object) {
ws.send(typeof data === "string" ? data : JSON.stringify(data));
}

return { send };
}
Then in your components:
const { send } = useWebSocket();
send({ message: 'Hello' });
const { send } = useWebSocket();
send({ message: 'Hello' });
This approach would give you proper TypeScript support without needing to augment type declarations. For more information on typing plugins in Nuxt, check the Nuxt documentation on plugins. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4d ago
<@260145305754927104> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Toffe
ToffeOP4d ago
Is composables creating a new connection for each inclusion or maintaining a global connection for the whole application? I need it to be running in the background with some logic for updating data real time to make the website real time when people edit stuff e.t.c. @kapa.ai
kapa.ai
kapa.ai4d ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Great question about composables and WebSocket connections in Nuxt! By default, when you create a WebSocket connection in a composable, it will create a new connection each time the composable is called in different components. This is because composables create isolated instances with their own state. According to the Stack Overflow post in the knowledge sources:
When you invoke useYourComposable() in ComponentOne.vue, and useYourComposable() in ComponentTwo.vue, two separate instances of the composable are created. Each instance has its own separate state. Changes to state in one instance won't affect the same state in the other instance.
Stack Overflow Post For a real-time application where you need a single WebSocket connection shared across your entire application, you have several options: 1. Use a Nuxt Plugin Instead Plugins in Nuxt run once at app initialization and can provide globally accessible functionality:
// plugins/websocket.js
export default defineNuxtPlugin(() => {
const ws = new WebSocket("ws://localhost:3000/websocket");

ws.onopen = () => console.log("Connected to WS");
ws.onmessage = (message) => {
const parsedData = JSON.parse(message.data);
console.log("Received data:", parsedData);
};

return {
provide: {
ws
}
};
});
// plugins/websocket.js
export default defineNuxtPlugin(() => {
const ws = new WebSocket("ws://localhost:3000/websocket");

ws.onopen = () => console.log("Connected to WS");
ws.onmessage = (message) => {
const parsedData = JSON.parse(message.data);
console.log("Received data:", parsedData);
};

return {
provide: {
ws
}
};
});
Then access it with useNuxtApp().$ws in your components. 2. Create a Singleton Pattern in Your Composable
// composables/useWebSocket.js
let ws = null;
export function useWebSocket() {
const nuxtApp = useNuxtApp();

if (!ws) {
ws = new WebSocket("ws://localhost:3000/websocket");

ws.onopen = () => console.log("Connected to WS");
ws.onmessage = (message) => {
const parsedData = JSON.parse(message.data);
console.log("Received data:", parsedData);
// You could call a function here to update your state
};

// Clean up the connection when the app is destroyed
nuxtApp.hook('app:destroyed', () => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.close();
ws = null;
}
});
}

function send(data) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(typeof data === "string" ? data : JSON.stringify(data));
}
}

return { ws, send };
}
// composables/useWebSocket.js
let ws = null;
export function useWebSocket() {
const nuxtApp = useNuxtApp();

if (!ws) {
ws = new WebSocket("ws://localhost:3000/websocket");

ws.onopen = () => console.log("Connected to WS");
ws.onmessage = (message) => {
const parsedData = JSON.parse(message.data);
console.log("Received data:", parsedData);
// You could call a function here to update your state
};

// Clean up the connection when the app is destroyed
nuxtApp.hook('app:destroyed', () => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.close();
ws = null;
}
});
}

function send(data) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(typeof data === "string" ? data : JSON.stringify(data));
}
}

return { ws, send };
}
3. Use Nitro's Built-in WebSocket Support Nuxt 3.11+ and Nitro 2.9+ have built-in WebSocket support that you can leverage:
// server/websocket/index.ts
export default defineWebSocketHandler({
open(peer) {
console.log('Connection opened');
},
message(peer, message) {
// Handle messages
peer.send({ type: 'update', data: 'Some data' });
},
close(peer) {
console.log('Connection closed');
}
});
// server/websocket/index.ts
export default defineWebSocketHandler({
open(peer) {
console.log('Connection opened');
},
message(peer, message) {
// Handle messages
peer.send({ type: 'update', data: 'Some data' });
},
close(peer) {
console.log('Connection closed');
}
});
Then on the client side, you can connect to this WebSocket endpoint. For more information on implementing WebSockets in Nuxt with Nitro, check out this YouTube tutorial which covers the implementation from scratch. Remember that for a production application, you'll also want to handle reconnection logic, authentication, and error handling for your WebSocket connections. Would you like more specific guidance on any of these approaches? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4d ago
<@260145305754927104> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Toffe
ToffeOP4d ago
@kapa.ai
No description
kapa.ai
kapa.ai4d ago
Let me check the knowledge base for you again. One moment! :books: Sorry @Toffe, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Toffe
ToffeOP4d ago
Any humans who can help with this? 🙂

Did you find this page helpful?