Skip to content

Instantly share code, notes, and snippets.

@rugk
Created August 19, 2025 20:17
Show Gist options
  • Select an option

  • Save rugk/d9009a50e6a0f7085af390c3b6d4fe57 to your computer and use it in GitHub Desktop.

Select an option

Save rugk/d9009a50e6a0f7085af390c3b6d4fe57 to your computer and use it in GitHub Desktop.
AI chat on how to do enums with proper type-enforcement through JSDoc in VSCode (Copilot - GPT 4.1)

rugk: > Argument of type 'string' is not assignable to parameter of type '{ [x: string]: string; }'.ts(2345)

for rowserCommunication.addListener

Now the issue is wrong JSDOC actually. See #file:BrowserCommunication.js * @param {COMMUNICATION_MESSAGE_TYPE} messageType type of message is wrong JSDOC. If you see #file:BrowserCommunicationTypes.js you see it is an object of strings, but i should just be used more or less like an enum (which JS itself does not have), so you provide a specific value, but cannot provide any arbotrary string.

How can I fix the JSDoc to represent that?

GitHub Copilot: You are correct: COMMUNICATION_MESSAGE_TYPE is an object (used as an enum), so the JSDoc type for parameters expecting a value from it should not be COMMUNICATION_MESSAGE_TYPE (the object), but rather a value from it.

To represent this in JSDoc, you can use:

  • keyof typeof COMMUNICATION_MESSAGE_TYPE (TypeScript only)
  • Or, for plain JSDoc, you can use a union of string literals, but that's verbose.
  • The most common and readable way in JSDoc is to use string and add a comment, or use @enum to define the possible values.

Best practice for JSDoc in this case: Define a typedef for the possible values:

/**
 * @typedef {"omnibarToggle"|"autocorrectBackground"|"autocorrectContent"|"contextMenu"|"insert"} CommunicationMessageType
 */

/**
 * An object of all available communication types.
 *
 * @public
 * @const
 * @type {Object.<string, string>}
 */
export const COMMUNICATION_MESSAGE_TYPE = Object.freeze({
    OMNIBAR_TOGGLE: "omnibarToggle",
    AUTOCORRECT_BACKGROUND: "autocorrectBackground",
    AUTOCORRECT_CONTENT: "autocorrectContent",
    CONTEXT_MENU: "contextMenu",
    INSERT: "insert"
});

Then, in your function JSDoc, use:

/**
 * Add a listener for a specific type.
 *
 * @public
 * @param {CommunicationMessageType} messageType type of message to receive
 * @param {listenerCallback} callback
 * @returns {void}
 */
export function addListener(messageType, callback) {
    // ...existing code...
}

Summary:

  • Define a @typedef for the allowed string values.
  • Use that typedef in your function JSDoc.

This will make your types clear and help with editor/type checking support.

Similar code found with 1 license type

rugk: A better solution than re-defining all values again with COMMUNICATION_MESSAGE_TYPE?

keyof typeof COMMUNICATION_MESSAGE_TYPE looks fine at first (in the method signature rendering by VSCOde), but at least in VSCode it does not enforce the type here. I can also enter a string and it does not complain.

See also #file:jsconfig.json

GitHub Copilot: You're correct:

  • COMMUNICATION_MESSAGE_TYPE is an object, not a TypeScript enum.
  • keyof typeof COMMUNICATION_MESSAGE_TYPE gives you the keys ("OMNIBAR_TOGGLE", etc.), not the values ("omnibarToggle", etc.).
  • But your API expects the values (e.g., "omnibarToggle"), not the keys.

Why does VSCode not enforce the type?

Because in plain JS with JSDoc, VSCode's type inference is limited.
Even with @param {keyof typeof COMMUNICATION_MESSAGE_TYPE} or similar, it can't fully enforce value types for object values, only for keys.

How to get value types from an object in JSDoc/JS?

There is no way in JSDoc to automatically infer the union of all values of an object.
You must either:

  • Manually define a typedef (as shown before), or
  • Use TypeScript, which can infer value types with typeof COMMUNICATION_MESSAGE_TYPE[keyof typeof COMMUNICATION_MESSAGE_TYPE].

Best practice for plain JS + JSDoc

Manual typedef is the only robust way:

/**
 * @typedef {"omnibarToggle"|"autocorrectBackground"|"autocorrectContent"|"contextMenu"|"insert"} CommunicationMessageType
 */
export const COMMUNICATION_MESSAGE_TYPE = Object.freeze({
    OMNIBAR_TOGGLE: "omnibarToggle",
    AUTOCORRECT_BACKGROUND: "autocorrectBackground",
    AUTOCORRECT_CONTENT: "autocorrectContent",
    CONTEXT_MENU: "contextMenu",
    INSERT: "insert"
});

And use it:

/**
 * @param {CommunicationMessageType} messageType
 */
export function addListener(messageType, callback) { ... }

TL;DR

  • There is no way to avoid re-defining the values in JSDoc.
  • TypeScript can infer value types, but JSDoc cannot.
  • Manual typedef is the only way for value enforcement in JS+JSDoc.

If you want true enforcement, migrate to TypeScript.
Otherwise, manual typedef is the best you can do in JS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment