Hooks and the SDKLesson 16 of 18

Useful Hooks

TypeScript Type Checker Hook

One of Claude Code's most common weaknesses is editing function signatures without updating all call sites. A post-tool use hook that runs the TypeScript compiler after every file edit creates an automatic feedback loop that catches these errors instantly.

Claude editsPost-Hook:tsc --noEmitPass: continueErrors -> ClaudeAuto-fix

Automatic type checking feedback loop

javascriptTypeScript type checker post-hook implementation
// hooks/type_check.js (post-tool use hook)
const { execSync } = require('child_process');
const fs = require('fs');

const input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf8'));
const filePath = input.tool_input?.file_path || '';

// Only check TypeScript files
if (!filePath.match(/\.(ts|tsx)$/)) process.exit(0);

try {
  execSync('npx tsc --noEmit 2>&1', { encoding: 'utf8' });
  process.exit(0);  // No errors
} catch (err) {
  // Type errors found -- feed them back to Claude
  console.error('Type errors detected after edit:\n' + err.stdout);
  process.exit(0);  // Post-hooks can't block, just provide feedback
}

Adaptable Pattern

This pattern works for any language with a type checker or linter. Python projects can run mypy or pyright, Go projects can run go vet, Rust can run cargo check. For untyped languages, run the test suite instead.

Duplicate Code Prevention Hook

When tackling complex, multi-step tasks, Claude sometimes creates new functions instead of reusing existing ones -- especially in directories with many similar utilities. A deduplication hook uses the Claude Code SDK to launch a second Claude instance that reviews changes for duplicates.

javascriptSDK-powered duplicate detection hook
// hooks/dedup_check.js (post-tool use hook)
const { query } = require('@anthropic-ai/claude-code');
const fs = require('fs');

const input = JSON.parse(fs.readFileSync('/dev/stdin', 'utf8'));
const filePath = input.tool_input?.file_path || '';

// Only check critical directories
if (!filePath.startsWith('src/queries/')) process.exit(0);

const result = await query({
  prompt: `Check if the changes in ${filePath} duplicate 
  any existing function in src/queries/. Report duplicates.`,
  cwd: process.cwd()
});

if (result.includes('duplicate')) {
  console.error('Duplicate detected:\n' + result);
  process.exit(2);  // Block the edit
}
⚠️

Cost-Performance Trade-off

The deduplication hook launches a separate Claude instance for every file edit in the watched directory. This adds time and API cost. Only apply it to critical directories (like shared utilities or database queries) where duplication is most harmful. Don't watch your entire codebase.

Key Takeaways

  • Type checker hook: run tsc --noEmit after every .ts/.tsx edit, feed errors back to Claude for auto-fixing
  • Deduplication hook: use the SDK to launch a review Claude that checks for duplicate functions
  • Both use the post-tool-use pattern to create automated feedback loops
  • The type checker pattern adapts to any language: mypy for Python, go vet for Go, cargo check for Rust
  • Only apply expensive hooks (like deduplication) to critical directories to balance cost and quality

Check Your Understanding

Test what you learned in this lesson.

Q1.The TypeScript type checker hook uses which hook type?

Q2.Why should the duplicate code prevention hook only watch critical directories?

Q3.You use Python instead of TypeScript. How would you adapt the type checker hook pattern?