hunter-party-ts/type-hunter-ts/SKILL.md
Audit TypeScript type definitions for design debt — duplicated shapes, missing derivations, over-engineered generics, under-constrained type parameters, reinvented utility types, and disorganized type architecture. Type structure and maintainability, not type enforcement. Use when: reviewing type definitions for maintainability, reducing type duplication, simplifying over-engineered type-level logic, or reorganizing type architecture after growth.
npx skillsauth add skyosev/agent-skills type-hunter-tsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Audit TypeScript type definitions for type design debt — places where types are duplicated instead of derived, generics are more complex than they need to be, built-in utilities are reinvented, or type organization has drifted. The goal: types are derived from single sources of truth, use the simplest constructs that work, and are easy to find and maintain.
Derive, don't duplicate. When two types share structure, one should be derived from the other using utility types
(Pick, Omit, Partial, &). Parallel type definitions that duplicate fields are a maintenance trap — a change
to the source shape must be replicated manually in every copy.
Simplest construct wins. If an interface works, don't use a mapped type. If a mapped type works, don't use a conditional type. Reach for advanced type-level constructs only when simpler alternatives fail. Type-level code is code — it must be readable and maintainable.
Constraints document intent. A generic parameter <T> accepts anything — it communicates nothing. <T extends Record<string, unknown>> tells the reader and the compiler what T must be. Every generic should have the tightest
constraint that works.
Generics must vary. A type parameter that is always instantiated with the same concrete type is indirection, not
abstraction. If Cache<T> is always Cache<User>, remove T and use User directly. Introduce generics when there
are 2+ distinct instantiations. Exception: library and public API types may carry generic parameters for consumer
flexibility even with a single internal instantiation — flag only when the generic adds no value to any consumer.
Reuse the standard library. TypeScript ships Partial, Required, Readonly, Pick, Omit, Record,
Extract, Exclude, NonNullable, ReturnType, Parameters, and more. Hand-rolling equivalents wastes tokens
and creates maintenance surface.
Types have a place. Shared domain types belong in a central location. Implementation-local types belong next to their code. A 500-line types file that mixes domain types with internal helpers is disorganized. A type defined in a function body that is used by three modules is misplaced.
Two or more type definitions that represent the same domain concept with the same or near-identical shape.
Signals:
Action: Identify the canonical source type. Derive variants using Pick, Omit, Partial, &, or mapped types.
Delete the duplicates.
Types that should be derived from a source type but are manually defined, creating drift risk.
Signals:
Pick<Entity, ...>)Partial<Entity>)readonly to each field manually (should be Readonly<Entity>)Action: Replace with the appropriate derivation. If no built-in utility fits, create a project-level utility type.
Conditional types, recursive types, or mapped types that are more complex than the problem requires.
Signals:
as key remapping where a simple Pick/Omit would workAction: Simplify to the least powerful construct that solves the problem. If the type-level logic is genuinely needed (library types, framework constraints), document why.
Generic type parameters with no extends constraint, or constraints too broad to be useful.
Signals:
<T> where T is always used in a context that assumes an object shape<T extends any> or <T extends unknown> — no constraint at all<T extends object> when only Record<string, string> is passedAction: Add the tightest constraint that matches actual usage. If the generic accepts only one type, remove it.
Generic parameters that don't vary, aren't used meaningfully, or exist only for theoretical extensibility.
Signals:
<T> is used in one field and nowhere elseAction: Remove the generic and use the concrete type directly. Reintroduce when a real second use case emerges.
Hand-rolled type utilities that replicate built-in TypeScript utility types.
Signals:
{ [K in keyof T]?: T[K] } instead of Partial<T>{ readonly [K in keyof T]: T[K] } instead of Readonly<T>{ [K in keyof T as K extends U ? K : never]: T[K] } instead of Pick<T, Extract<keyof T, U>>Exclude/Extract implementations using conditional typesNonNullable or ReturnType definitionsAction: Replace with the built-in. If the custom version has genuinely different semantics, document the difference.
Opportunities to use satisfies, as const, or const type parameters to improve type safety without adding complexity.
Signals:
satisfies would catch typos without widening the typeas const, losing literal type informationconst type parameters would preserve literal inferencez.infer<> but instead maintain a
parallel manual typeAction: Apply satisfies for shape validation at assignment. Use as const on fixed data. Use const type
parameters where literal inference matters. Derive types from schemas instead of maintaining parallel definitions.
Type definitions that have drifted into the wrong locations or accumulated into unwieldy files.
Signals:
types.ts file with 300+ lines mixing domain types, DTOs, internal helpers, and utility typesAction: Collocate implementation-local types with their code. Centralize shared domain types. Split god type files by domain concept. Ensure one canonical import path per type.
main/master)BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main)
SCOPE=$(git diff --name-only $(git merge-base HEAD $BASE)...HEAD)
Constrain all subsequent scans to the resolved surface.EXCLUDE='--glob !**/*.test.* --glob !**/*.spec.* --glob !**/node_modules/** --glob !**/dist/**'
# Type definitions (interfaces, type aliases)
rg '(interface|type)\s+\w+' --type ts $EXCLUDE
# Generic type parameters
rg --pcre2 '<\w+(\s+extends\s+\w+)?' --type ts $EXCLUDE
# Conditional types (nested extends with ?)
rg --pcre2 'extends\s+.*\?\s+' --type ts $EXCLUDE
# Mapped types
rg '\[.*\s+in\s+keyof' --type ts $EXCLUDE
# Utility type usage (to measure adoption vs hand-rolling)
rg '(Partial|Required|Readonly|Pick|Omit|Record|Exclude|Extract|NonNullable|ReturnType|Parameters)<' --type ts $EXCLUDE
# Large type files
rg -c '(interface|type)\s+\w+' --type ts $EXCLUDE --sort path
# satisfies usage (adoption check)
rg 'satisfies\s' --type ts $EXCLUDE
# as const usage
rg 'as\s+const\b' --type ts $EXCLUDE
For each generic type: Is the constraint tight? Does the parameter vary? Is a simpler construct available? For each conditional/mapped type: Is this the simplest approach? Could a utility type replace it? For each hand-rolled utility: Does a built-in equivalent exist?
Save as YYYY-MM-DD-type-hunter-audit-{$LLM-name}.md in the project's docs folder (or project root if no docs folder exists).
# Type Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
## Findings
### Type Duplication
| # | Types | Locations | Overlap | Action |
| - | ----- | --------- | ------- | ------ |
| 1 | `User`, `UserDTO` | file:line, file:line | 8/10 fields identical | Derive DTO with `Omit<User, 'password'>` |
### Missing Derivations
| # | Type | Location | Source Type | Action |
| - | ---- | -------- | ----------- | ------ |
| 1 | `UserSummary` | file:line | `User` | Replace with `Pick<User, 'id' \| 'name'>` |
### Over-Engineered Types
| # | Type | Location | Complexity | Action |
| - | ---- | -------- | ---------- | ------ |
| 1 | `DeepMerge<A, B>` | file:line | 4-level conditional + recursion | Replace with `A & B` (sufficient for actual usage) |
### Under-Constrained Generics
| # | Type/Function | Location | Parameter | Action |
| - | ------------- | -------- | --------- | ------ |
| 1 | `cache<T>()` | file:line | `T` (no constraint) | Add `T extends Record<string, unknown>` |
### Phantom Type Parameters
| # | Type | Location | Parameter | Instantiations | Action |
| - | ---- | -------- | --------- | -------------- | ------ |
| 1 | `Store<T>` | file:line | `T` | Always `User` | Remove generic, use `User` directly |
### Reinvented Utilities
| # | Type | Location | Built-In Equivalent | Action |
| - | ---- | -------- | ------------------- | ------ |
| 1 | `MakeOptional<T>` | file:line | `Partial<T>` | Replace with built-in |
### Type Organization
| # | File | Location | Issue | Action |
| - | ---- | -------- | ----- | ------ |
| 1 | `types.ts` | file:line | 400 lines, mixes domain + internal types | Split by domain concept |
## Recommendations (Priority Order)
1. **Must-fix**: {type duplication with drift risk, phantom generics adding complexity}
2. **Should-fix**: {missing derivations, reinvented utilities, under-constrained generics}
3. **Consider**: {type organization, over-engineered types with limited usage}
file/path.ext:line with the exact type definition.development
Transforms vague feature ideas into precise, codebase-grounded technical requirements. Use when requirements are ambiguous/incomplete, the user struggles to describe behavior, terminology is unclear, or multiple concepts are mixed. Output is a requirements spec—NOT an implementation plan.
development
Audit TypeScript test code for quality gaps — missing coverage on critical paths, brittle tests coupled to implementation, over-mocking, assertion-free tests, missing edge cases, and duplicated test setup. Focuses on test effectiveness, not production code structure. Use when: reviewing TypeScript test suites for reliability, reducing false-positive test failures, improving coverage of critical business logic, or cleaning up test debt.
tools
Audit TypeScript class and interface design for SOLID violations — god classes, rigid extension points, broken substitutability, fat interfaces, and concrete dependency chains. Focuses on responsibility assignment and abstraction fitness. Use when: reviewing class hierarchies, preparing for extension with new variants, reducing coupling between services, or improving testability of class-heavy code.
development
Audit TypeScript code for classic code smells — feature envy, data clumps, shotgun surgery, primitive obsession, temporal coupling, comments as deodorant, temporary fields, callback hell, enum abuse, and class abuse. Use when: reviewing TypeScript code for structural design problems, preparing for a refactor, auditing code after rapid feature development, or hunting for misplaced responsibilities.