TypeScript Documentation
Documentation tooling for TypeScript projects — JSDoc comment patterns that surface in IDE tooltips, TypeDoc configuration for generating API documentation sites, Architectural Decision Record (ADR) templates for recording design decisions, and framework-specific documentation patterns (NestJS, React, Angular/Compodoc). Use when writing JSDoc for a public API, setting up TypeDoc to generate documentation from source, recording why a technical decision was made, or documenting a framework-specific pattern.
npx skills add github:pantheon-org/tekhne --skill development--typescript-advanced--docsDocumentation tooling and conventions for TypeScript codebases: JSDoc, TypeDoc, ADRs, and framework-specific patterns.
Mindset
Write documentation that a compiler-checked type can’t already express — a JSDoc @param restating a parameter’s type the signature already shows is noise; a JSDoc comment explaining why a parameter exists, what invariant it must satisfy, or what happens at its edge cases earns its place. Treat an ADR as a record for a future reader who wasn’t in the room, not a justification written for the reviewer who is: state the decision, the alternatives considered, and the consequences accepted, in that order. Generated documentation (TypeDoc output) is only as good as the JSDoc it’s generated from — investing in TypeDoc configuration without investing in the comments it reads from produces a well-formatted site with nothing useful in it.
When to Apply
Use this skill when:
- Writing JSDoc comments for a public API surface
- Configuring TypeDoc to generate a documentation site from TypeScript source
- Recording an architectural decision with an ADR
- Documenting a framework-specific pattern (NestJS decorators, React components, Angular/Compodoc)
Use When
- “How should I document this public function/class with JSDoc?”
- “How do I set up TypeDoc to generate our API docs?”
- “How do I write an ADR for this decision?”
- “What’s the convention for documenting a NestJS/React/Angular component?”
Scope
In Scope
- JSDoc comment patterns and best practices.
- TypeDoc configuration and setup.
- ADR templates and structure.
- Framework-specific documentation patterns (NestJS, React, Angular).
Out of Scope
- The type-first design decisions an ADR might record — see the sibling
typescript-practicesskill for the workflow itself. - Generating documentation content from types mechanically covered elsewhere (e.g. what a mapped type does) — see the sibling
typescript-type-systemskill for the mechanics; this skill covers how to document the result.
When NOT to Use
Do not use this skill for deciding what a type or pattern should do — it covers documenting a decision or API, not making one. Do not use it as a substitute for actually running npx typedoc and checking the generated output — a JSDoc comment that looks right can still render incorrectly (a malformed @example block, a broken {@link}).
Quick Commands
Generate Docs
npx typedoc --out docs src/index.ts
Anti-Patterns
NEVER write a JSDoc comment that only restates the type signature
WHY: a comment that duplicates what the signature already says adds reading overhead with no new information, and drifts out of sync with the signature over time.
BAD:
/**
* @param name - a string
* @returns a string
*/
function greet(name: string): string { return `Hello, ${name}`; }
GOOD:
/**
* Formats a greeting for display. Falls back to "Guest" if `name` is empty,
* since an empty greeting would render as blank in the header component.
*/
function greet(name: string): string {
return `Hello, ${name || "Guest"}`;
}
NEVER write an ADR that only justifies a decision already made
WHY: an ADR’s value is to a future reader deciding whether the reasoning still holds; a one-sided justification omits the alternatives and trade-offs that reader needs to judge that.
BAD: “We chose Zod because it’s good.” (no alternatives, no consequences, no context)
GOOD: A record with Context (what problem existed), Decision (what was chosen), Alternatives Considered (what else, and why not), and Consequences (what this commits the codebase to).
NEVER configure TypeDoc without first ensuring the source has real JSDoc to generate from
WHY: TypeDoc renders whatever comments exist; a well-configured generator over undocumented source produces a polished site of empty pages.
BAD: Running npx typedoc --out docs src/index.ts as the first and only documentation step on undocumented code.
GOOD: Add JSDoc to the public API surface first, then configure and run TypeDoc to publish it.
References
| File | Covers |
|---|---|
references/jsdoc-patterns.md |
JSDoc comment best practices and patterns |
references/typedoc-config.md |
TypeDoc configuration and setup |
references/adr-templates.md |
Architectural Decision Record templates |
references/framework-docs.md |
Framework-specific documentation patterns (NestJS, React, Angular/Compodoc) |
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
No eval scenarios available.
Architectural Decision Records (ADR) Templates
ADR Template
# ADR-{number}: {Title}
**Status:** {Proposed | Accepted | Deprecated | Superseded}
**Date:** {YYYY-MM-DD}
**Deciders:** {List of people involved}
**Technical Story:** {Link to issue/ticket}
## Context
What is the issue we're facing in a given context?
- What are the current constraints?
- What are the requirements?
- What forces are at play (technical, business, political)?
## Decision
What is the change we're proposing/making?
- Be specific and concrete
- Use active voice: "We will..."
## Consequences
What becomes easier or more difficult to do because of this change?
### Positive
- Benefit 1
- Benefit 2
### Negative
- Drawback 1
- Drawback 2
### Neutral
- Implication 1
## Alternatives Considered
What other options did we evaluate?
### Alternative 1: {Name}
- Description
- Pros
- Cons
- Why rejected
### Alternative 2: {Name}
- Description
- Pros
- Cons
- Why rejected
## Compliance
How will we ensure this decision is followed?
- Code review checks
- Automated tests
- Linting rules
- Documentation updates
## References
- [Link to relevant docs]
- [Link to research]
- [Link to related ADRs]
Example: State Management ADR
# ADR-003: Use Zustand for Client State Management
**Status:** Accepted
**Date:** 2024-01-15
**Deciders:** Frontend Team
**Technical Story:** #234
## Context
Our React application currently uses Redux Toolkit for state management. As the app grows, we're experiencing:
- Boilerplate overhead (actions, reducers, selectors)
- Difficulty with TypeScript inference
- Complex async logic with thunks
- Large bundle size (~50KB)
We need a simpler, more TypeScript-friendly state management solution that:
- Reduces boilerplate
- Has excellent TypeScript support
- Is lightweight (<5KB)
- Handles async state easily
## Decision
We will adopt Zustand as our primary client state management library.
```typescript
// Simple, typed store
import { create } from 'zustand';
interface TodoStore {
todos: Todo[];
addTodo: (text: string) => void;
removeTodo: (id: string) => void;
}
export const useTodoStore = create<TodoStore>((set) => ({
todos: [],
addTodo: (text) =>
set((state) => ({
todos: [...state.todos, { id: nanoid(), text, done: false }],
})),
removeTodo: (id) =>
set((state) => ({
todos: state.todos.filter((todo) => todo.id !== id),
})),
}));
```
## Consequences
### Positive
- **Less boilerplate**: No actions, reducers, or providers
- **Better TypeScript**: Automatic type inference
- **Smaller bundle**: 3KB vs 50KB (94% reduction)
- **Simpler async**: No middleware needed
- **Easier testing**: Stores are just functions
### Negative
- **Team learning curve**: New library to learn
- **Migration effort**: Need to migrate existing Redux stores
- **Middleware ecosystem**: Smaller than Redux
- **DevTools**: Less mature than Redux DevTools
### Neutral
- **No Provider**: Stores are hooks, not context
- **Multiple stores**: Encouraged over single store
## Alternatives Considered
### Alternative 1: Keep Redux Toolkit
- **Pros**: Team knows it, robust ecosystem, proven at scale
- **Cons**: Heavy boilerplate, bundle size, TypeScript friction
- **Why rejected**: Pain points outweigh familiarity
### Alternative 2: Jotai
- **Pros**: Atom-based, very TypeScript-friendly, tiny bundle
- **Cons**: Atom composition can be complex, less intuitive API
- **Why rejected**: Learning curve steeper than Zustand
### Alternative 3: Valtio
- **Pros**: Proxy-based reactivity, minimal API
- **Cons**: Proxy compatibility issues, less predictable updates
- **Why rejected**: Prefer explicit update model
## Compliance
- **Code review**: Ensure new state uses Zustand patterns
- **Migration plan**: Migrate one store per sprint
- **Documentation**: Create usage guide with TypeScript examples
- **Linting**: Add ESLint rule to prevent new Redux stores
- **Testing**: Document testing patterns for Zustand stores
## References
- [Zustand Documentation](https://docs.pmnd.rs/zustand)
- [Bundle Size Comparison](https://bundlephobia.com/package/zustand)
- [TypeScript Guide](https://docs.pmnd.rs/zustand/guides/typescript)
- [Migration from Redux](https://docs.pmnd.rs/zustand/guides/migration)
Example: TypeScript Configuration ADR
# ADR-007: Enable Strict TypeScript Mode
**Status:** Accepted
**Date:** 2024-02-01
**Deciders:** Engineering Team
**Technical Story:** #456
## Context
Our TypeScript configuration currently has:
- `strict: false`
- Mix of implicit `any` types
- Nullable values not handled consistently
- Type errors caught late in CI
This leads to:
- Runtime errors that TypeScript should catch
- Unclear function contracts
- Difficult refactoring
## Decision
We will enable strict mode in `tsconfig.json`:
```json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
```
## Consequences
### Positive
- **Fewer runtime errors**: Catch nulls, undefined access
- **Better refactoring**: Type system catches breaking changes
- **Clearer contracts**: Function signatures more explicit
- **Improved IDE support**: Better autocomplete and hints
### Negative
- **Migration effort**: Fix ~500 existing type errors
- **Slower initial development**: More type annotations needed
- **Learning curve**: Team needs to understand strict mode
### Neutral
- **More verbose**: Some type annotations required
- **Third-party types**: May need `@ts-expect-error` for bad types
## Alternatives Considered
### Alternative 1: Gradual adoption
- Enable one strict flag at a time
- **Why rejected**: Piecemeal approach drags out migration
### Alternative 2: Stay non-strict
- Continue with `strict: false`
- **Why rejected**: Technical debt accumulates
## Compliance
- **Migration plan**: Fix errors by module over 4 sprints
- **CI enforcement**: Block merges with type errors
- **Documentation**: Create strict mode guide
- **Pair programming**: Help team learn strict patterns
## References
- [TypeScript Strict Mode](https://www.typescriptlang.org/tsconfig#strict)
- [Migrating to Strict](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html)
Best Practices
- Number ADRs sequentially - Easy to reference
- Update status - Mark when superseded
- Document context fully - Explain constraints and forces
- Be specific - Concrete decisions, not vague statements
- List alternatives - Show due diligence
- Include code examples - Show how decision manifests in code
- Define compliance - Make decision enforceable
- Link to references - Provide supporting material
- Keep concise - 1-2 pages maximum
- Commit to repo - Version control decisions
Framework-Specific Documentation Patterns
NestJS Documentation
/**
* Authentication guard that validates JWT tokens
*
* @guard JwtAuthGuard
* @usageNotes
* Apply this guard to controllers or routes that require authentication:
*
* @example
* ```typescript
* @Controller('users')
* @UseGuards(JwtAuthGuard)
* export class UsersController {
* @Get()
* findAll() {
* return this.usersService.findAll();
* }
* }
* ```
*
* @security JWT
* @throws {UnauthorizedException} When token is invalid or expired
*/
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
/**
* Custom decorator to extract user from request
*
* @decorator CurrentUser
* @usageNotes
* Use this decorator to access the authenticated user in route handlers
*
* @example
* ```typescript
* @Get('profile')
* @UseGuards(JwtAuthGuard)
* getProfile(@CurrentUser() user: User) {
* return user;
* }
* ```
*/
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
}
);
React Component Documentation
/**
* Reusable button component with various styles and sizes
*
* @component Button
* @param {ButtonProps} props - Component props
* @param {ReactNode} props.children - Button content
* @param {'primary' | 'secondary' | 'danger'} props.variant - Visual style
* @param {'small' | 'medium' | 'large'} props.size - Button size
* @param {boolean} props.disabled - Whether button is disabled
* @param {() => void} props.onClick - Click handler
*
* @example
* ```tsx
* // Primary button
* <Button variant="primary" onClick={handleSubmit}>
* Submit
* </Button>
*
* // Disabled secondary button
* <Button variant="secondary" disabled>
* Cancel
* </Button>
* ```
*
* @accessibility
* - Uses semantic <button> element
* - Disabled state communicated via aria-disabled
* - Keyboard accessible with Space/Enter keys
*/
export const Button: React.FC<ButtonProps> = ({
children,
variant = 'primary',
size = 'medium',
disabled = false,
onClick,
}) => {
// ...
};
/**
* Custom hook for managing form state
*
* @hook useForm
* @template T - Form values type
* @param {T} initialValues - Initial form values
* @param {(values: T) => void} onSubmit - Submit handler
* @returns {UseFormReturn<T>} Form state and handlers
*
* @example
* ```tsx
* const { values, errors, handleChange, handleSubmit } = useForm(
* { email: '', password: '' },
* (values) => {
* api.login(values);
* }
* );
* ```
*/
export const useForm = <T extends Record<string, unknown>>(
initialValues: T,
onSubmit: (values: T) => void
): UseFormReturn<T> => {
// ...
};
Express Middleware Documentation
/**
* Rate limiting middleware to prevent API abuse
*
* @middleware rateLimiter
* @param {RateLimitOptions} options - Rate limit configuration
* @param {number} options.windowMs - Time window in milliseconds
* @param {number} options.maxRequests - Maximum requests per window
*
* @errorResponses
* - 429 Too Many Requests - Rate limit exceeded
*
* @example
* ```typescript
* app.use('/api', rateLimiter({
* windowMs: 15 * 60 * 1000, // 15 minutes
* maxRequests: 100
* }));
* ```
*
* @performance
* Uses in-memory store by default. For production, use Redis store:
* ```typescript
* rateLimiter({
* store: new RedisStore({ client: redisClient }),
* windowMs: 15 * 60 * 1000,
* maxRequests: 100
* })
* ```
*/
export const rateLimiter = (options: RateLimitOptions): RequestHandler => {
// ...
};
/**
* Error handling middleware
*
* @middleware errorHandler
* @param {Error} err - Error object
* @param {Request} req - Express request
* @param {Response} res - Express response
* @param {NextFunction} next - Next middleware
*
* @errorResponses
* - 400 Bad Request - Validation error
* - 401 Unauthorized - Authentication error
* - 404 Not Found - Resource not found
* - 500 Internal Server Error - Unhandled error
*
* @example
* ```typescript
* // Must be last middleware
* app.use(errorHandler);
* ```
*/
export const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
// ...
};
Angular Service Documentation
/**
* Service for managing user authentication
*
* @injectable UserService
* @providedIn 'root'
*
* @usageNotes
* Inject this service into components that need authentication:
*
* @example
* ```typescript
* @Component({ ... })
* export class LoginComponent {
* constructor(private userService: UserService) {}
*
* login() {
* this.userService.login(this.credentials).subscribe({
* next: (user) => console.log('Logged in', user),
* error: (err) => console.error('Login failed', err)
* });
* }
* }
* ```
*/
@Injectable({
providedIn: 'root',
})
export class UserService {
/**
* Authenticates user with credentials
*
* @param {Credentials} credentials - User credentials
* @returns {Observable<User>} Observable that emits authenticated user
* @throws {HttpErrorResponse} When authentication fails
*/
login(credentials: Credentials): Observable<User> {
// ...
}
}
Vue Composable Documentation
/**
* Composable for fetching and caching data
*
* @composable useFetch
* @template T - Response data type
* @param {string} url - API endpoint URL
* @param {FetchOptions} options - Fetch configuration
* @returns {UseFetchReturn<T>} Reactive fetch state
*
* @example
* ```vue
* <script setup>
* const { data, loading, error, refetch } = useFetch('/api/users');
* </script>
*
* <template>
* <div v-if="loading">Loading...</div>
* <div v-else-if="error">Error: {{ error }}</div>
* <ul v-else>
* <li v-for="user in data" :key="user.id">{{ user.name }}</li>
* </ul>
* </template>
* ```
*/
export const useFetch = <T>(
url: string,
options?: FetchOptions
): UseFetchReturn<T> => {
// ...
};
Best Practices
- Framework-specific tags - Use
@component,@hook,@middleware, etc. - Usage notes - Show real-world usage in context
- Framework conventions - Follow framework documentation patterns
- Integration examples - Show how pieces fit together
- Lifecycle notes - Document when hooks/lifecycle methods run
- DI/Props documentation - Clearly document dependencies
- State management - Explain state flow and updates
Common Patterns
/**
* @component
* @hook
* @middleware
* @guard
* @decorator
* @injectable
* @composable
* @providedIn
* @usageNotes
* @accessibility
* @performance
* @errorResponses
*/
JSDoc Best Practices and Patterns
JSDoc comments provide runtime documentation for TypeScript code. They appear in IDE tooltips, generated documentation, and help other developers understand your code.
When to Write JSDoc
Always document:
- Public API functions, classes, interfaces
- Complex algorithms or business logic
- Non-obvious behavior or side effects
- Security-sensitive code
- Deprecated APIs
Don’t document:
- Self-explanatory code where the type signature is sufficient
- Private implementation details
- Obvious getters/setters
- Simple utility functions with clear names
Basic Structure
/**
* Brief one-line summary
*
* Optional longer description with multiple paragraphs.
* Can include markdown formatting.
*
* @param paramName - Description of parameter
* @param anotherParam - Another parameter description
* @returns Description of return value
* @throws {ErrorType} Description of when this error is thrown
*
* @example
* ```typescript
* const result = functionName('value');
* ```
*/
function functionName(paramName: string, anotherParam: number): ResultType {
// implementation
}
Documenting Functions
/**
* Authenticates a user and returns access tokens
*
* @param email - User's email address
* @param password - User's password (will be hashed)
* @returns Authentication result with access and refresh tokens
* @throws {InvalidCredentialsError} If email or password is incorrect
* @throws {AccountLockedError} If account has been locked due to failed attempts
*
* @example
* ```typescript
* const result = await authenticateUser('user@example.com', 'password123');
* console.log(result.accessToken);
* ```
*/
async function authenticateUser(
email: string,
password: string
): Promise<AuthResult> {
// implementation
}
Documenting Classes
/**
* Service for managing user authentication and authorization
*
* @remarks
* This service handles JWT-based authentication, password hashing,
* and role-based access control. All passwords are hashed with bcrypt
* using a cost factor of 12.
*
* @example
* ```typescript
* const authService = new AuthService(config);
* const token = await authService.login(email, password);
* const user = await authService.verifyToken(token);
* ```
*/
export class AuthService {
/**
* Creates a new authentication service
*
* @param config - Service configuration including JWT secret and token expiry
*/
constructor(private config: AuthConfig) {}
/**
* Authenticates a user and returns access tokens
*
* @param credentials - User login credentials
* @returns Authentication result with tokens
* @throws {InvalidCredentialsError} If credentials are invalid
*/
async login(credentials: LoginCredentials): Promise<AuthResult> {
// implementation
}
}
Documenting Interfaces and Types
/**
* Represents a user in the system
*
* @property id - Unique user identifier (UUID v4)
* @property email - User's email address (must be verified)
* @property role - User's role for access control
* @property createdAt - When the user account was created
*/
interface User {
id: string;
email: string;
role: 'admin' | 'user' | 'guest';
createdAt: Date;
}
/**
* Configuration options for the authentication service
*/
interface AuthConfig {
/**
* Secret key for signing JWT tokens
* @remarks Should be at least 32 characters and stored securely
*/
jwtSecret: string;
/**
* How long access tokens are valid (in seconds)
* @default 3600
*/
tokenExpiry?: number;
}
Documenting Generic Types
/**
* Wraps a value with loading and error states
*
* @template T - The type of the wrapped data
*
* @example
* ```typescript
* type UserAsync = Async<User>;
* const loading: UserAsync = { status: 'loading' };
* const success: UserAsync = { status: 'success', data: user };
* ```
*/
type Async<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error };
Common JSDoc Tags
| Tag | Purpose | Example |
|---|---|---|
@param |
Document function parameter | @param userId - The user's ID |
@returns |
Document return value | @returns The user object |
@throws |
Document exceptions thrown | @throws {NotFoundError} If user not found |
@example |
Provide usage example | @example const x = foo('bar'); |
@remarks |
Additional detailed info | @remarks This function is rate limited |
@see |
Link to related documentation | @see {@link AuthService} |
@deprecated |
Mark as deprecated | @deprecated Use newFunction instead |
@default |
Document default value | @default 3600 |
@template |
Document generic type parameter | @template T - The item type |
@internal |
Mark as internal API | @internal |
@beta |
Mark as beta/unstable | @beta |
Linking to Other Symbols
/**
* Retrieves a user by ID
*
* @param userId - The user ID to look up
* @returns The user object
* @throws {NotFoundError} If user doesn't exist
*
* @see {@link User} for the user type definition
* @see {@link createUser} for creating new users
*/
function getUser(userId: string): Promise<User> {
// implementation
}
Documenting Complex Examples
/**
* Processes a batch of user updates with transaction support
*
* @param updates - Array of user updates to process
* @returns Results array with success/failure for each update
*
* @example Basic usage
* ```typescript
* const updates = [
* { id: '1', name: 'Alice' },
* { id: '2', email: 'bob@example.com' }
* ];
* const results = await processBatchUpdates(updates);
* ```
*
* @example With error handling
* ```typescript
* try {
* const results = await processBatchUpdates(updates);
* const failures = results.filter(r => !r.success);
* if (failures.length > 0) {
* console.error('Some updates failed:', failures);
* }
* } catch (error) {
* console.error('Batch update failed:', error);
* }
* ```
*/
async function processBatchUpdates(
updates: UpdateUser[]
): Promise<BatchResult[]> {
// implementation
}
Deprecation Notices
/**
* @deprecated Since v2.0.0. Use {@link authenticateUser} instead.
*
* This function will be removed in v3.0.0.
*
* Migration guide:
* ```typescript
* // Old
* const user = await login(email, password);
*
* // New
* const result = await authenticateUser(email, password);
* const user = result.user;
* ```
*/
function login(email: string, password: string): Promise<User> {
// implementation
}
Security Documentation
/**
* Stores a user's password hash in the database
*
* @param userId - The user's ID
* @param password - Plain text password (will be hashed before storage)
*
* @security
* - Password is hashed using bcrypt with cost factor 12
* - Original password is never stored
* - Function should only be called over secure connections
*
* @remarks
* This function should be called during user registration and password reset flows.
* It automatically salts the password.
*/
async function storePassword(userId: string, password: string): Promise<void> {
// implementation
}
ESLint Integration
Enforce JSDoc with ESLint:
{
"rules": {
"jsdoc/require-jsdoc": ["warn", {
"require": {
"FunctionDeclaration": true,
"ClassDeclaration": true,
"MethodDefinition": true
}
}],
"jsdoc/require-param": "warn",
"jsdoc/require-returns": "warn",
"jsdoc/require-description": "warn"
}
}
References
TypeDoc Configuration and Setup
TypeDoc generates API documentation from TypeScript source code and JSDoc comments. It produces clean, searchable HTML or Markdown documentation.
Installation
npm install --save-dev typedoc
# Optional plugins
npm install --save-dev typedoc-plugin-markdown
npm install --save-dev typedoc-plugin-missing-exports
Basic Configuration
Create typedoc.json in your project root:
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["src/index.ts"],
"out": "docs/api",
"excludePrivate": true,
"excludeProtected": false,
"excludeInternal": true,
"readme": "README.md",
"plugin": []
}
Configuration Options
Entry Points
Single entry point:
{
"entryPoints": ["src/index.ts"]
}
Multiple entry points:
{
"entryPoints": [
"src/index.ts",
"src/auth/index.ts",
"src/database/index.ts"
]
}
Glob patterns:
{
"entryPointStrategy": "expand",
"entryPoints": ["src/**/*.ts"]
}
Output Configuration
{
"out": "docs/api",
"theme": "default",
"name": "My Project API",
"includeVersion": true,
"disableSources": false,
"sourceLinkTemplate": "https://github.com/user/repo/blob/{gitRevision}/{path}#L{line}"
}
Visibility Control
{
"excludePrivate": true, // Exclude private members
"excludeProtected": false, // Include protected members
"excludeInternal": true, // Exclude @internal tagged items
"excludeExternals": true, // Exclude external modules
"excludeNotDocumented": false // Include items without docs
}
Module Organization
{
"categorizeByGroup": true, // Group by @group tags
"defaultCategory": "Other", // Default category name
"categoryOrder": [ // Category display order
"Core",
"Authentication",
"Database",
"*"
]
}
Using @group and @category Tags
Organize documentation with JSDoc tags:
/**
* @group Authentication
*/
export class AuthService { }
/**
* @group Database
*/
export class UserRepository { }
/**
* @category Utilities
*/
export function formatDate(date: Date): string { }
Markdown Output
For Markdown output (useful for static site generators):
{
"plugin": ["typedoc-plugin-markdown"],
"out": "docs/api",
"theme": "markdown",
"entryDocument": "index.md",
"hideInPageTOC": false
}
Package.json Scripts
{
"scripts": {
"docs": "typedoc",
"docs:watch": "typedoc --watch",
"docs:serve": "typedoc && npx http-server docs/api"
}
}
Advanced Configuration
Custom Theme
{
"theme": "./custom-theme",
"customCss": "./docs/custom.css"
}
Navigation Configuration
{
"navigation": {
"includeCategories": true,
"includeGroups": true,
"includeFolders": false
},
"navigationLinks": {
"GitHub": "https://github.com/user/repo",
"NPM": "https://www.npmjs.com/package/my-package"
}
}
Search Configuration
{
"searchInComments": true,
"searchInDocuments": true,
"searchGroupBoosts": {
"Core": 2.0,
"Authentication": 1.5
}
}
Monorepo Configuration
For monorepo setups with multiple packages:
{
"entryPoints": [
"packages/core/src/index.ts",
"packages/auth/src/index.ts",
"packages/database/src/index.ts"
],
"entryPointStrategy": "packages"
}
Validation Configuration
{
"validation": {
"notExported": true, // Warn about non-exported types
"invalidLink": true, // Warn about invalid @link references
"notDocumented": false // Warn about undocumented items
},
"requiredToBeDocumented": [
"Class",
"Function",
"Method"
]
}
CI/CD Integration
GitHub Actions
name: Generate Docs
on:
push:
branches: [main]
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run docs
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/api
GitLab CI
pages:
stage: deploy
script:
- npm ci
- npm run docs
- mv docs/api public
artifacts:
paths:
- public
only:
- main
Performance Optimization
For large codebases:
{
"excludeNotDocumented": true,
"excludePrivate": true,
"excludeInternal": true,
"cacheBust": true,
"cleanOutputDir": true
}
Complete Example Configuration
{
"$schema": "https://typedoc.org/schema.json",
"name": "My Project API Documentation",
"entryPoints": ["src/index.ts"],
"entryPointStrategy": "resolve",
"out": "docs/api",
"theme": "default",
"readme": "README.md",
"includeVersion": true,
"excludePrivate": true,
"excludeProtected": false,
"excludeInternal": true,
"excludeNotDocumented": false,
"categorizeByGroup": true,
"defaultCategory": "Other",
"categoryOrder": [
"Core",
"Authentication",
"Database",
"*"
],
"searchInComments": true,
"validation": {
"notExported": true,
"invalidLink": true,
"notDocumented": false
},
"navigation": {
"includeCategories": true,
"includeGroups": true
},
"navigationLinks": {
"GitHub": "https://github.com/user/repo",
"NPM": "https://www.npmjs.com/package/my-package"
},
"plugin": ["typedoc-plugin-missing-exports"],
"sourceLinkTemplate": "https://github.com/user/repo/blob/{gitRevision}/{path}#L{line}"
}
Troubleshooting
Issue: Types not appearing
- Check
excludePrivate,excludeInternalsettings - Ensure types are exported from entry point
- Use
--logLevel Verbosefor debugging
Issue: Slow generation
- Enable
excludeNotDocumented - Reduce entry points
- Use
--skipErrorCheckingfor quicker builds
Issue: Broken links
- Enable
validation.invalidLink - Use
@linkinstead of markdown links for type references - Check that linked symbols are exported