
writing-jsdoc
by jasonkuhrt
Tool configurations
SKILL.md
name: writing-jsdoc description: This skill should be used when the user asks to "add JSDoc", "document this function", "write JSDoc comments", "link to a type", "use @link tag", "hyperlink in JSDoc", "reference external service in docs", or needs guidance on JSDoc syntax, clickable type references, or TypeScript-optimized documentation.
Writing JSDoc
TypeScript-optimized JSDoc documentation guidance. TypeScript provides types; JSDoc provides meaning.
CRITICAL: No Type Repetition
TypeScript already has complete type information. Never duplicate it in JSDoc.
// ❌ BAD - Redundant type information
/**
* @param {string} name - The user's name
* @param {number} age - The user's age
* @returns {boolean} Whether the user is valid
*/
function isValidUser(name: string, age: number): boolean { ... }
// ✅ GOOD - Description only, types come from TS
/**
* Validates user meets minimum requirements.
* @param name - Display name (must be non-empty)
* @param age - Must be 13 or older
*/
function isValidUser(name: string, age: number): boolean { ... }
Forbidden in TS projects:
| Tag | Why Forbidden |
|---|---|
@type | TS has the type |
@param {Type} | TS has param types |
@returns {Type} | TS has return type |
@typedef | Use TS type or interface |
@callback | Use TS function types |
What to Document
Document what TypeScript cannot express:
| Document | Example |
|---|---|
| Purpose/intent | "Validates user meets minimum requirements" |
| Constraints | "Must be non-empty", "Range: 0-100" |
| Side effects | "Writes to localStorage", "Mutates input" |
| Exceptions | @throws - TS doesn't track thrown errors |
| Defaults | @default - Runtime default values |
| Examples | @example - Usage patterns |
| Deprecation | @deprecated - Migration guidance |
| External refs | @see - Links to docs, specs |
What NOT to Document
Skip JSDoc entirely for:
- Self-evident code -
getName()returning a name needs no docs - Internal helpers - Private functions used in one place
- Type-only exports - Types are self-documenting
- Trivial getters/setters -
get id() { return this._id; }
JSDoc Placement Rules
DON'T Add JSDoc To
- Namespace exports (
export * as Name) - Barrel exports (
export * from './foo') - Re-exports
- Implementations that inherit documentation from their interface/type
- Multiple JSDoc blocks for the same declaration (only closest one is effective)
Avoid Duplicate JSDoc
If a const implements an interface with JSDoc, don't repeat it on the const:
/** Creates a user validator. */
interface UserValidator {
validate(user: User): boolean;
}
// ❌ BAD - Duplicates interface JSDoc
/** Creates a user validator. */
const validator: UserValidator = { ... };
// ✅ GOOD - Interface JSDoc is inherited
const validator: UserValidator = { ... };
Namespace Export Hack
For export * as Name, use @ts-expect-error with duplicate namespace to add docs:
// @ts-expect-error Duplicate identifier
export * as Utils from './utils'
/** Utility functions for string manipulation. */
export namespace Utils {}
Useful Tags for TypeScript
@throws
TypeScript doesn't track exceptions. Document them:
/**
* Parses JSON configuration file.
* @throws {SyntaxError} If JSON is malformed
* @throws {Error} If file doesn't exist
*/
function parseConfig(path: string): Config { ... }
@example
Show usage patterns, especially for complex APIs:
/**
* Creates a debounced version of a function.
* @example
* const debouncedSave = debounce(save, 300);
* input.addEventListener('change', debouncedSave);
*/
function debounce<T extends (...args: any[]) => any>(fn: T, ms: number): T { ... }
@default
Document runtime defaults (complements TS default params):
interface Options {
/** @default 3000 */
timeout?: number
/** @default 'warn' */
logLevel?: 'debug' | 'info' | 'warn' | 'error'
}
@deprecated
Always include migration path:
/**
* @deprecated Use {@link createUser} instead. Will be removed in v3.0.
*/
function addUser(name: string): User { ... }
@typeParam - Use Sparingly
Only document type parameters users explicitly provide:
// ✅ Document - user explicitly provides type
/**
* Creates a type-safe event emitter.
* @typeParam Events - Map of event names to payload types
* @example
* const emitter = createEmitter<{ click: MouseEvent; key: KeyboardEvent }>();
*/
function createEmitter<
Events extends Record<string, unknown>,
>(): Emitter<Events>
// ❌ Don't document - type is inferred from arguments
/**
* Returns first element of array.
*/
function first<T>(arr: T[]): T | undefined
The {@link} Tag
Create clickable references to types, functions, and identifiers.
Syntax
/** Composes {@link RhythmMediaPlayPauseButton} and {@link RhythmMediaTimeDisplay}. */
/** Returns result from {@link audiosByPriority | the priority list}. */
Import Requirement
For {@link Identifier} to be clickable, the identifier must be in scope:
| Scenario | Clickable? |
|---|---|
| Same file export | Yes |
| Imported type | Yes |
| Not imported | No (plain text) |
JSDoc-Only Imports
When a type is needed only for {@link}, use underscore prefix to avoid lint errors:
// For JSDoc {@link} only
import type { MuxAsset as _MuxAsset } from '@heartbeat/types';
/**
* Uploads audio to {@link _MuxAsset | Mux} for transcoding.
*/
export const uploadVoiceNote = async (blob: Blob) => { ... };
External Links
@see for Reference Docs
/**
* Type-safe predicates for DOMException error types.
* @see https://developer.mozilla.org/en-US/docs/Web/API/DOMException
*/
Markdown for Inline Links
/**
* Uploads audio to [Mux](https://www.mux.com/) for transcoding.
*/
Quick Reference
| Use Case | Syntax |
|---|---|
| Link to identifier | {@link Identifier} |
| Link with text | {@link Identifier | display text} |
| External docs | @see https://... |
| Inline link | [Text](https://...) |
| Exception | @throws {ErrorType} Description |
| Example | @example + code block |
| Default value | @default value |
| Deprecation | @deprecated Use X instead |
| JSDoc-only import | import type { X as _X } |
Notes
- TypeScript provides types; JSDoc provides meaning and context
{@link}requires identifier in scope (imported or same file)- Underscore-prefixed imports avoid unused-vars lint errors
- Links render as clickable in VSCode, Zed, and modern IDEs
- Focus on what TS cannot express: intent, constraints, exceptions
- See
limitations.mdfor known TypeScript/IDE limitations
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
1ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です



