Standards & Reference
Code Documentation
This document defines the code documentation standards using TSDoc syntax.
Table of Contents
- Overview
- TSDoc vs JSDoc
- Required Documentation
- TSDoc Syntax Reference
- Documentation Patterns
- React Component Documentation
- Hook Documentation
- Type Documentation
- Documentation Generation
- Coverage Enforcement
- IDE Integration
- Best Practices
Overview
Purpose
Code documentation serves multiple critical functions:
- Developer Experience: Enables IntelliSense and hover documentation in IDEs
- Onboarding: Helps new team members understand the codebase
- API Reference: Generates browsable API documentation
- Maintenance: Preserves intent and context for future changes
- Quality Gate: Ensures thoughtful API design
Scope
TSDoc documentation is required for:
- All exported functions, classes, and types
- React components and their props
- Custom hooks and their parameters/return values
- Complex internal logic requiring explanation
TSDoc vs JSDoc
We use TSDoc syntax, which extends JSDoc with TypeScript-specific features:
| Feature | JSDoc | TSDoc |
|---|---|---|
| Basic tags (@param, @returns) | Yes | Yes |
| Type annotations in comments | Required | Optional (use TypeScript types) |
| @remarks tag | No | Yes |
| @packageDocumentation | No | Yes |
| {@link} inline references | Limited | Full support |
| @public/@internal/@beta | No | Yes |
Key Difference
// JSDoc - types in comments (don't do this)
/**
* @param {string} name - User name
* @returns {boolean}
*/
function validate(name) { ... }
// TSDoc - types in TypeScript (do this)
/**
* Validates user name format.
* @param name - User name to validate
* @returns True if name is valid
*/
function validate(name: string): boolean { ... }
Required Documentation
Coverage Requirements
| Element | Coverage | Enforcement |
|---|---|---|
| Exported Functions | 100% | Block PR |
| Exported Classes | 100% | Block PR |
| Exported Types/Interfaces | 100% | Block PR |
| React Components | 100% | Block PR |
| Custom Hooks | 100% | Block PR |
| Component Props | 100% | Block PR |
| Internal Functions (complex) | 80% | Warning |
| Internal Functions (simple) | Optional | None |
What Requires Documentation
// REQUIRED - Exported function
/**
* Calculates the total cost including tax.
* @param items - Line items to calculate
* @param taxRate - Tax rate as decimal (0.08 = 8%)
* @returns Total cost with tax applied
*/
export function calculateTotal(items: LineItem[], taxRate: number): number { ... }
// REQUIRED - Exported type
/**
* Represents a line item in an invoice.
*/
export interface LineItem {
/** Unique identifier for the line item */
id: string;
/** Product or service description */
description: string;
/** Unit price in cents */
unitPrice: number;
/** Quantity ordered */
quantity: number;
}
// OPTIONAL - Simple internal helper
function addNumbers(a: number, b: number): number {
return a + b;
}
// REQUIRED - Complex internal logic
/**
* Applies progressive discount tiers based on total quantity.
*
* @remarks
* Discount tiers:
* - 0-9 items: 0%
* - 10-49 items: 5%
* - 50-99 items: 10%
* - 100+ items: 15%
*
* @param quantity - Total quantity across all items
* @param subtotal - Subtotal before discount
* @returns Discount amount in cents
*/
function calculateTierDiscount(quantity: number, subtotal: number): number { ... }
TSDoc Syntax Reference
Standard Tags
| Tag | Purpose | Example |
|---|---|---|
@param | Document function parameter | @param name - User's display name |
@returns | Document return value | @returns User object or null if not found |
@throws | Document thrown exceptions | @throws {@link ValidationError} When input invalid |
@example | Provide usage example | See below |
@remarks | Additional context | Extended explanation |
@see | Cross-reference | @see {@link RelatedFunction} |
@deprecated | Mark as deprecated | @deprecated Use newFunction instead |
Visibility Tags
| Tag | Purpose |
|---|---|
@public | Part of public API |
@internal | Internal implementation detail |
@beta | Unstable API, may change |
@alpha | Experimental, not for production |
Inline Tags
| Tag | Purpose | Example |
|---|---|---|
{@link} | Link to symbol | {@link UserService} |
{@inheritDoc} | Inherit documentation | {@inheritDoc BaseClass.method} |
Complete Example
/**
* Fetches user data from the API with caching support.
*
* @remarks
* This function implements a stale-while-revalidate caching strategy.
* Cached results are returned immediately while a background refresh occurs.
*
* The cache TTL is configurable via the options parameter but defaults
* to 5 minutes for optimal performance.
*
* @example
* Basic usage:
* ```typescript
* const user = await fetchUser('user-123');
* console.log(user.name);
* ```
*
* @example
* With caching options:
* ```typescript
* const user = await fetchUser('user-123', {
* cacheTTL: 60000, // 1 minute
* forceRefresh: true
* });
* ```
*
* @param userId - The unique identifier of the user to fetch
* @param options - Optional configuration for the request
* @param options.cacheTTL - Cache time-to-live in milliseconds (default: 300000)
* @param options.forceRefresh - Skip cache and fetch fresh data (default: false)
* @returns Promise resolving to the user object
* @throws {@link NotFoundError} When user does not exist
* @throws {@link NetworkError} When API is unreachable
*
* @see {@link UserService} for batch user operations
* @see {@link cacheManager} for cache configuration
*
* @public
*/
export async function fetchUser(
userId: string,
options?: FetchUserOptions
): Promise<User> {
// Implementation
}
Documentation Patterns
Function Documentation
/**
* Brief description of what the function does.
*
* @param paramName - Description of parameter
* @returns Description of return value
*/
export function functionName(paramName: ParamType): ReturnType { ... }
Function with Options Object
/**
* Creates a new event with the specified configuration.
*
* @param config - Event configuration options
* @param config.name - Display name for the event
* @param config.startDate - Event start date
* @param config.endDate - Event end date (optional, defaults to startDate)
* @param config.portfolio - Portfolio assignment
* @returns The created event object
*/
export function createEvent(config: CreateEventConfig): Event { ... }
Async Function
/**
* Submits an approval request for the specified deliverable.
*
* @remarks
* Sends email notification to all assigned approvers.
* Creates audit trail entry for compliance tracking.
*
* @param deliverableId - ID of the deliverable requiring approval
* @param approvers - List of user IDs to request approval from
* @returns Promise resolving to the created approval request
* @throws {@link ValidationError} When deliverable is already approved
* @throws {@link PermissionError} When user lacks submission rights
*/
export async function submitApproval(
deliverableId: string,
approvers: string[]
): Promise<ApprovalRequest> { ... }
Class Documentation
/**
* Manages event data persistence and retrieval.
*
* @remarks
* Uses Parse Server as the backend. Implements caching for
* frequently accessed events using a LRU strategy.
*
* @example
* ```typescript
* const service = new EventService();
* const events = await service.getByPortfolio('domestic');
* ```
*/
export class EventService {
/**
* Creates an instance of EventService.
* @param config - Optional service configuration
*/
constructor(config?: EventServiceConfig) { ... }
/**
* Retrieves all events for a portfolio.
* @param portfolio - Portfolio identifier
* @returns Promise resolving to array of events
*/
async getByPortfolio(portfolio: string): Promise<Event[]> { ... }
}
React Component Documentation
Component with Props Interface
/**
* Displays event details in a card format.
*
* @remarks
* Supports multiple display modes and integrates with the
* event context for state management.
*
* @example
* ```tsx
* <EventCard
* event={event}
* variant="compact"
* onSelect={handleSelect}
* />
* ```
*/
export interface EventCardProps {
/** The event data to display */
event: Event;
/** Display variant */
variant?: 'default' | 'compact' | 'detailed';
/** Callback when card is selected */
onSelect?: (event: Event) => void;
/** Additional CSS class names */
className?: string;
}
export function EventCard({
event,
variant = 'default',
onSelect,
className
}: EventCardProps): JSX.Element {
// Implementation
}
Component with Children
/**
* Provides event context to child components.
*
* @example
* ```tsx
* <EventProvider eventId="evt-123">
* <EventDetails />
* <EventTimeline />
* </EventProvider>
* ```
*/
export interface EventProviderProps {
/** Event ID to load */
eventId: string;
/** Child components that consume the event context */
children: React.ReactNode;
}
export function EventProvider({ eventId, children }: EventProviderProps): JSX.Element { ... }
Compound Component
/**
* Tabbed interface for organizing content.
*
* @example
* ```tsx
* <Tabs defaultValue="details">
* <Tabs.List>
* <Tabs.Trigger value="details">Details</Tabs.Trigger>
* <Tabs.Trigger value="timeline">Timeline</Tabs.Trigger>
* </Tabs.List>
* <Tabs.Content value="details">
* <EventDetails />
* </Tabs.Content>
* <Tabs.Content value="timeline">
* <EventTimeline />
* </Tabs.Content>
* </Tabs>
* ```
*/
export interface TabsProps {
/** Default active tab value */
defaultValue?: string;
/** Controlled active tab value */
value?: string;
/** Callback when active tab changes */
onValueChange?: (value: string) => void;
/** Tab content */
children: React.ReactNode;
}
Hook Documentation
Basic Hook
/**
* Manages event list state with pagination and filtering.
*
* @remarks
* Uses React Query for server state management. Automatically
* refetches when filters change. Implements optimistic updates
* for improved UX.
*
* @example
* ```typescript
* const { events, isLoading, setFilter, refetch } = useEventList({
* initialFilter: { status: 'active' }
* });
* ```
*
* @param options - Hook configuration
* @returns Event list state and controls
*/
export function useEventList(options?: UseEventListOptions): UseEventListResult {
// Implementation
}
/**
* Configuration options for useEventList hook.
*/
export interface UseEventListOptions {
/** Initial filter criteria */
initialFilter?: EventFilter;
/** Page size for pagination */
pageSize?: number;
/** Enable real-time updates */
enableRealtime?: boolean;
}
/**
* Return value from useEventList hook.
*/
export interface UseEventListResult {
/** List of events matching current filter */
events: Event[];
/** Loading state */
isLoading: boolean;
/** Error if fetch failed */
error: Error | null;
/** Current filter criteria */
filter: EventFilter;
/** Update filter criteria */
setFilter: (filter: EventFilter) => void;
/** Force refetch */
refetch: () => Promise<void>;
/** Pagination controls */
pagination: PaginationState;
}
Hook with Complex Return
/**
* Manages form state for event editing with validation.
*
* @remarks
* Integrates with React Hook Form and Zod validation.
* Handles dirty state tracking and unsaved changes warning.
*
* @example
* ```typescript
* const {
* register,
* handleSubmit,
* errors,
* isDirty,
* isSubmitting
* } = useEventForm(eventId);
*
* return (
* <form onSubmit={handleSubmit(onSubmit)}>
* <input {...register('name')} />
* {errors.name && <span>{errors.name.message}</span>}
* </form>
* );
* ```
*
* @param eventId - ID of event to edit, or undefined for new event
* @returns Form state and controls
*/
export function useEventForm(eventId?: string): UseEventFormResult { ... }
Type Documentation
Interface
/**
* Represents an event in the dashboard.
*
* @remarks
* Events are the primary entity in the system. They represent
* tradeshows, conferences, and other marketing activities.
*/
export interface Event {
/** Unique identifier */
id: string;
/** Display name */
name: string;
/** Event start date (ISO 8601) */
startDate: string;
/** Event end date (ISO 8601) */
endDate: string;
/**
* Current status
* @see {@link EventStatus} for possible values
*/
status: EventStatus;
/** Portfolio assignment */
portfolio: Portfolio;
/** Tier level (1-4) */
tier: 1 | 2 | 3 | 4;
/** Budget in USD cents */
budget?: number;
/** Creation timestamp */
createdAt: string;
/** Last update timestamp */
updatedAt: string;
}
Enum
/**
* Possible event statuses.
*
* @remarks
* Status transitions follow a linear progression:
* Draft -> Planning -> Active -> Complete
*
* Cancelled can be set from any status.
*/
export enum EventStatus {
/** Initial state, not visible to most users */
Draft = 'draft',
/** Active planning phase */
Planning = 'planning',
/** Currently running */
Active = 'active',
/** Successfully finished */
Complete = 'complete',
/** Terminated early */
Cancelled = 'cancelled'
}
Type Alias
/**
* Portfolio categories for event classification.
*
* @remarks
* - Domestic portfolios serve US-based events
* - International portfolios serve non-US events
*/
export type Portfolio =
| 'domestic-commercial'
| 'domestic-defense'
| 'domestic-services'
| 'international-commercial'
| 'international-defense'
| 'international-services';
Union Type
/**
* Possible results from an API operation.
*
* @remarks
* Uses discriminated union pattern for type-safe error handling.
*
* @example
* ```typescript
* const result = await createEvent(data);
* if (result.success) {
* console.log('Created:', result.data.id);
* } else {
* console.error('Failed:', result.error.message);
* }
* ```
*/
export type ApiResult<T> =
| { success: true; data: T }
| { success: false; error: ApiError };
Documentation Generation
TypeDoc Configuration
Create typedoc.json in the project root:
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": ["src/index.ts"],
"out": "docs/api",
"plugin": ["typedoc-plugin-markdown"],
"excludePrivate": true,
"excludeInternal": true,
"readme": "none",
"categoryOrder": [
"Components",
"Hooks",
"Services",
"Types",
"Utilities"
]
}
NPM Scripts
Add to package.json:
{
"scripts": {
"docs:generate": "typedoc",
"docs:api": "typedoc --out docs/api",
"docs:coverage": "typedoc --validation.notExported --validation.notDocumented",
"docs:lint": "tsc --noEmit && eslint src --ext .ts,.tsx --rule '@typescript-eslint/no-floating-promises: error'",
"docs:watch": "typedoc --watch"
}
}
Generation Commands
# Generate API documentation
npm run docs:generate
# Generate with coverage validation
npm run docs:coverage
# Watch mode during development
npm run docs:watch
Output Structure
docs/
├── api/
│ ├── index.md
│ ├── modules.md
│ ├── classes/
│ │ └── EventService.md
│ ├── interfaces/
│ │ ├── Event.md
│ │ └── EventCardProps.md
│ ├── functions/
│ │ └── calculateTotal.md
│ └── types/
│ └── Portfolio.md
Coverage Enforcement
ESLint Rules
Add to .eslintrc.js:
module.exports = {
plugins: ['jsdoc'],
rules: {
'jsdoc/require-jsdoc': ['warn', {
publicOnly: true,
require: {
FunctionDeclaration: true,
MethodDefinition: true,
ClassDeclaration: true,
ArrowFunctionExpression: false,
FunctionExpression: false
}
}],
'jsdoc/require-param': 'warn',
'jsdoc/require-param-description': 'warn',
'jsdoc/require-returns': 'warn',
'jsdoc/require-returns-description': 'warn',
'jsdoc/check-param-names': 'error',
'jsdoc/check-tag-names': 'error',
'jsdoc/valid-types': 'off' // TypeScript handles types
}
};
CI Pipeline
# .github/workflows/docs.yml
name: Documentation
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Lint documentation
run: npm run docs:lint
- name: Check documentation coverage
run: npm run docs:coverage
- name: Generate documentation
run: npm run docs:generate
- name: Upload documentation
uses: actions/upload-artifact@v4
with:
name: api-docs
path: docs/api/
Coverage Thresholds
| Level | Threshold | Action |
|---|---|---|
| Public API | 100% | Block merge |
| Internal API | 80% | Warning |
| Overall | 90% | CI failure |
IDE Integration
VS Code Extensions
Install these extensions for optimal TSDoc support:
| Extension | Purpose |
|---|---|
| Document This | Generate TSDoc templates |
| TSDoc Comment | Syntax highlighting |
| Better Comments | Categorized comments |
| Error Lens | Inline lint errors |
Settings
Add to .vscode/settings.json:
{
"docthis.includeAuthorTag": false,
"docthis.includeDescriptionTag": true,
"docthis.enableHungarianNotationRecognition": false,
"editor.quickSuggestions": {
"comments": true
}
}
Snippets
Add to .vscode/typescript.code-snippets:
{
"TSDoc Function": {
"prefix": "tsdf",
"body": [
"/**",
" * ${1:Description}",
" *",
" * @param ${2:param} - ${3:Parameter description}",
" * @returns ${4:Return description}",
" */"
]
},
"TSDoc Component": {
"prefix": "tsdc",
"body": [
"/**",
" * ${1:Component description}",
" *",
" * @example",
" * ```tsx",
" * <${2:Component} ${3:prop}={${4:value}} />",
" * ```",
" */"
]
},
"TSDoc Hook": {
"prefix": "tsdh",
"body": [
"/**",
" * ${1:Hook description}",
" *",
" * @example",
" * ```typescript",
" * const { ${2:result} } = use${3:Hook}(${4:options});",
" * ```",
" *",
" * @param ${5:options} - ${6:Options description}",
" * @returns ${7:Return description}",
" */"
]
}
}
Best Practices
Do
- Write for humans first - Documentation should be readable and helpful
- Be specific - "Validates email format using RFC 5322" is better than "Validates email"
- Include examples - Show how to use the API with realistic code
- Document edge cases - What happens with null, empty arrays, etc.?
- Keep it current - Update docs when behavior changes
- Use cross-references - Link related functions and types
Don't
- Don't repeat the type - TypeScript already shows the type
- Don't state the obvious - "Sets the name" for setName() adds no value
- Don't use implementation details - "Uses regex internally" is irrelevant
- Don't forget @throws - Document exceptions that callers must handle
- Don't mix styles - Use TSDoc consistently, not JSDoc
Examples of Good vs Bad
// BAD - States the obvious
/**
* Gets user.
* @param id - ID
* @returns User
*/
function getUser(id: string): User { ... }
// GOOD - Adds value
/**
* Retrieves a user by their unique identifier.
*
* @remarks
* Results are cached for 5 minutes. Use forceRefresh option
* to bypass the cache when fresh data is required.
*
* @param id - The user's unique identifier (UUID format)
* @returns The user object, or null if not found
* @throws {@link NetworkError} When API is unreachable
*/
function getUser(id: string): User | null { ... }
Documentation Checklist
Before submitting code for review, verify:
- [ ] All exported functions have TSDoc comments
- [ ] All exported types/interfaces have TSDoc comments
- [ ] All React components have documented props
- [ ] All custom hooks have documented parameters and return values
- [ ] Complex logic has @remarks explaining the approach
- [ ] Public APIs have @example blocks with realistic usage
- [ ] Exceptions are documented with @throws
- [ ] Deprecated code uses @deprecated with migration path
- [ ] @param descriptions are meaningful, not just type names
- [ ] @returns descriptions explain what is returned, not just the type
- [ ] Cross-references use {@link} where helpful
- [ ] No TSDoc linting errors
Related Documents
- FLOWSTATE.md - Code documentation coverage requirements
- QUALITY.md - Code quality standards
- PROCESS.md - Development workflow
Last Updated: 2026-01-15