# Hook payloads typed

- **Pattern:** `ab-002079` (`plugin-extension-architecture.hook-system.type-safety`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002079
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002079)

## Why it matters

Untyped hook payloads — parameters typed as `any`, `unknown`, or `Record<string, any>` — eliminate compile-time safety from every plugin in the ecosystem. Plugin authors get no autocompletion, no error on typos in property names, and no signal when a host update renames a payload field. Breakage appears only at runtime, often in production. ISO 25010 maintainability.analysability scores fall sharply when the API surface cannot be understood without running code; typed payloads are the primary mechanism for making plugin APIs analysable.

## Severity rationale

High because untyped hook payloads remove compile-time safety from the entire plugin ecosystem, causing payload-shape mismatches to surface only as runtime errors in plugin authors' production deployments.

## Remediation

Define a typed map of all hook payloads and use generic registration to infer types at call sites. This converts payload shape errors from runtime crashes into compile-time failures, and provides IDE autocompletion to every plugin author.

```typescript
interface HookPayloads {
  'before:request': { request: Request; config: PluginConfig };
  'after:response': { request: Request; response: Response; duration: number };
  'on:error': { error: Error; request: Request; retryCount: number };
}

on<K extends keyof HookPayloads>(
  hook: K,
  handler: (payload: HookPayloads[K]) => void | Promise<void>
): void;
```

## Detection

- **ID:** `type-safety`
- **Severity:** `high`
- **What to look for:** Check whether hook handler parameters (the "payload" or "context" passed to each handler) are typed with TypeScript interfaces, generics, or JSDoc annotations. Look for:
  1. TypeScript interface/type for each hook's payload (e.g., `interface BeforeRequestContext { url: string; headers: Headers }`)
  2. Generic registration that infers payload types: `hooks.on<BeforeRequest>('before:request', (ctx) => { /* ctx is typed */ })`
  3. A central type definition file or module that exports all hook payload types
  4. JSDoc `@param` annotations on hook invocations if not using TypeScript
  Without typed payloads, plugin authors must read host source code to understand what data their handlers receive, which is the top cause of broken plugins after host updates.
- **Pass criteria:** Count all plugin API surface types. Hook payloads are typed — either via TypeScript interfaces/generics, JSDoc annotations, or JSON Schema definitions. Plugin authors get autocompletion and compile-time errors when accessing payload properties. At minimum, the most commonly used hooks have typed payloads. At least 90% of plugin API methods must have TypeScript type definitions. Report the count of typed vs untyped API surface methods even on pass.
- **Fail criteria:** Quote the untyped API methods or missing type definitions found. Hook payloads are `any`, `unknown`, or untyped objects. Plugin authors receive no type information about what properties are available on the context/payload object. OR payloads are typed as a loose `Record<string, any>` that provides no useful type narrowing.
- **Skip (N/A) when:** The project does not use TypeScript and has no type system. Also skip for Python projects that don't use type hints, Go projects (strongly typed by default), and Rust projects (strongly typed by default).
- **Detail on fail:** `"Hook handlers receive a context parameter typed as 'any'. Plugin authors get no autocompletion, no compile-time errors for typos, and no documentation of available properties. The BeforeRequest hook passes {request, response, config} but this is only discoverable by reading the host's invokeHook() source code."`
- **Remediation:** Typed hook payloads are the most impactful improvement for plugin developer experience. They enable autocompletion, catch errors at compile time, and serve as inline documentation.

  ```typescript
  // Define payload types per hook:
  interface HookPayloads {
    'before:request': { request: Request; config: PluginConfig };
    'after:response': { request: Request; response: Response; duration: number };
    'on:error': { error: Error; request: Request; retryCount: number };
  }

  // Generic registration infers types:
  on<K extends keyof HookPayloads>(
    hook: K,
    handler: (payload: HookPayloads[K]) => void | Promise<void>
  ): void;
  ```

## External references

- iso-25010 maintainability.modifiability
- iso-25010 maintainability.analysability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-002079
