Hook payloads typed
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.
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:
- TypeScript interface/type for each hook's payload (e.g.,
interface BeforeRequestContext { url: string; headers: Headers }) - Generic registration that infers payload types:
hooks.on<BeforeRequest>('before:request', (ctx) => { /* ctx is typed */ }) - A central type definition file or module that exports all hook payload types
- JSDoc
@paramannotations 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.
- TypeScript interface/type for each hook's payload (e.g.,
-
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 looseRecord<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.
// 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:2011 · maintainability.modifiability — Modifiability — typed hook payloads surface breaking API changes at compile time
- iso-25010:2011 · maintainability.analysability — Analysability — typed payloads enable static analysis and IDE tooling
Taxons
History
- 2026-04-18·v1.0.0·Initial import from plugin-extension-architecture·automated