typescript-any-eliminator
Use when TypeScript source contains explicit `any` in application code, shared helpers, DTOs, or API layers that should be narrowed without changing runtime behavior — or when a reviewer or lint rule flags unsafe `any` usage.
Version
1.0.0
Maturity
draft
Repository
agent-skills
License
GNU GPL v3
Skill metadata
SKILL.md
TypeScript any eliminator
Use this skill when
- The user asks to remove, reduce, or replace
anytypes in TypeScript code. - You find explicit
anyin application code, shared helpers, DTOs, or API layers that should be narrowed safely. - The task is to improve type safety without changing runtime behavior.
Do not use this skill when
- The task is broad TypeScript type-system work that is not mainly about eliminating
any. - The
anyappears in generated code or third-party declarations that should not be hand-edited. - The repository intentionally documents a permissive boundary and the task is not to tighten it.
- The work primarily requires behavior-changing refactors rather than truthful type replacement.
Inputs to gather
Required before editing
- The targeted
anysites and the surrounding API surface. - The repository's typecheck command, such as
tsc --noEmitor an equivalent project wrapper. - Existing shared domain types, DTOs, schemas, validators, or helper types that may already model the value.
- Whether each boundary is trusted or untrusted.
Helpful if present
- Nearby tests or examples that show the intended shape.
- Whether the touched file is generated or backed by third-party typings.
- Existing lint rules or conventions around
unknown, guards, and assertions.
First move
- Inventory the targeted
anysites and read the adjacent types before editing. - Run the repository's typecheck command if available so you understand the current failure surface.
- Start with the smallest self-contained
anysite and consult the reference files before inventing new patterns.
Stop here if the boundary is honest
Do not force a fake precise type when the right answer is to keep the boundary permissive or move the fix closer to the real source.
- keep
unknownplus a guard when the value is untrusted - leave generated or vendor-owned files alone
- stop when the change would become a broader TypeScript redesign instead of a truthful
anyreplacement - report the boundary plainly instead of rationalizing a wider type as "good enough"
Workflow
- Trace inbound and outbound values so the replacement type matches real usage.
- Reuse an existing domain type, DTO, schema, validator, or helper type before creating a new local type.
- Choose the narrowest truthful replacement strategy:
- an existing shared type
- a generic with constraints
- a discriminated union or other explicit union
unknownplus narrowing or validation at an untrusted boundary
- If the honest boundary is still permissive, stop at
unknownplus a guard instead of inventing a fake precise type. - Update adjacent call sites only when needed to keep the typed surface coherent.
- Preserve runtime behavior while tightening the types.
- Re-run the typecheck and targeted tests after the change.
Outputs
anyusages replaced with precise types (inferred or explicit) in application code, helpers, DTOs, or API layers.- No runtime behavior change; type errors or tests confirm safety.
Guardrails
- Must not replace
anywith an equally vague type unless that is the honest boundary. - Must not use unsafe casts such as
as unknown asto silence the compiler. - Must not move
anyelsewhere to make the local error disappear. - Should prefer
unknownonly at untrusted boundaries, then narrow with guards or validators. - Should keep edits small and local unless a larger refactor is required to keep types coherent.
- May leave a documented or generated
anyboundary in place when editing it would be unsafe or out of scope. - Red flag: if the first safe fix is a schema, parser, or shared type already used elsewhere, reuse that instead of inventing a new local shape.
- Handoff trigger: if the change requires runtime validation work outside this file, route that boundary work to the relevant schema or validator owner.
Routing boundary
- Route to
schema-boundary-typingwhen truthfulanyremoval depends on runtime validation at an untrusted input boundary. - Route to
type-test-authoringafter the type surface is truthful and you need compile-time regression coverage for inference or assignability. - If compiler failures remain after
anycleanup, route causal error triage totsc-error-triage.
Validation
Re-run the repository's typecheck command after the edit.
Run targeted tests or the nearest equivalent validation command for the touched surface when available.
Confirm the final type is more precise than
any,object,Function, or another unjustified widening.Smoke test:
- should trigger: "Replace any in this API helper with a truthful type."
- should not trigger: "Add runtime validation for this untrusted JSON input." (→
schema-boundary-typing)
Examples
Beforeexport function read(input: any) { return input.user.id; }Afterexport function read(input: unknown) { if (!isUser(input)) throw new Error("invalid user"); return input.user.id; }Beforetype Payload = Record<string, any>;Aftertype Payload = Record<string, unknown>;
Support-file examples
- Reuse and constrained-generic before/after patterns live in
references/replacement-patterns.md. - Honest stop cases for generated code, vendor declarations, and intentionally unstructured boundaries live in
references/boundary-triage.md.
Reference files
references/replacement-patterns.md- before/after replacement patterns, including generic keyed access andunknownplus type-guard examples.references/boundary-triage.md- how to choose between existing types, unions,unknown, guards, and validators.references/unsafe-shortcuts-to-avoid.md- anti-patterns that hide type problems instead of fixing them.