Agent skill
typescript-guardian
Enforces TypeScript strict mode compliance, eliminates any types, ensures type safety, and maintains strict linting standards. Use when TypeScript errors occur, any types need elimination, or strict mode compliance is required.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/typescript-guardian
SKILL.md
TypeScript Guardian
Quick Start
This skill enforces TypeScript strict mode and type safety:
- Strict mode compliance: Verify all strict flags enabled
- Any type elimination: Replace
anywith specific types,unknown, or generics - Return type annotations: Add explicit return types to all functions
- Type narrowing: Implement type guards and predicates
When to Use
- TypeScript compiler errors need resolution
anytypes detected in production code- Missing return type annotations
- Strict mode violations (unchecked index access, null/undefined)
Strict Mode Configuration
Current tsconfig.json requirements (already enforced):
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"strictNullChecks": true,
"noImplicitAny": true,
"strictFunctionTypes": true
}
}
Verification commands:
# Check for TypeScript errors
npx tsc --noEmit
# Run type-aware linting
npm run lint:ci
Common Violations & Fixes
1. Implicit Any
// ❌ Implicit any
function process(data) {
return data.value;
}
// ✅ Explicit types
function process(data: { value: string }): string {
return data.value;
}
2. Unchecked Index Access
// ❌ No check for undefined
const users = ['Alice', 'Bob'];
const firstUser = users[0]; // string | undefined
console.log(firstUser.toUpperCase()); // Runtime error if empty
// ✅ Check for undefined
const firstUser = users[0];
if (firstUser !== undefined) {
console.log(firstUser.toUpperCase());
}
// ✅ Or use optional chaining
console.log(users[0]?.toUpperCase());
3. Null/Undefined Not Checked
// ❌ Assuming value exists
interface User {
profile?: { name: string };
}
function greet(user: User): string {
return `Hello, ${user.profile.name}`; // Error: profile might be undefined
}
// ✅ Check for existence
function greet(user: User): string {
if (user.profile === undefined) {
return 'Hello, stranger';
}
return `Hello, ${user.profile.name}`;
}
// ✅ Or use optional chaining
function greet(user: User): string {
return `Hello, ${user.profile?.name ?? 'stranger'}`;
}
Any Type Elimination
Detection Strategy
# Find any types in source code
grep -r ": any" src/ --include="*.ts" --include="*.tsx" --exclude="*.test.ts"
# Find any[] array types
grep -r ": any\\[\\]" src/
# Find type assertions with any
grep -r "as any" src/
ESLint enforces these rules (already configured):
@typescript-eslint/no-explicit-any: error@typescript-eslint/no-unsafe-assignment: error@typescript-eslint/no-unsafe-call: error
Replacement Patterns
Replace Any with Unknown:
// ❌ any (no type safety)
function processData(data: any): void {
console.log(data.value);
}
// ✅ unknown (requires type narrowing)
function processData(data: unknown): void {
if (typeof data === 'object' && data !== null && 'value' in data) {
console.log((data as { value: string }).value);
}
}
// ✅ Best: specific type
interface Data {
value: string;
}
function processData(data: Data): void {
console.log(data.value);
}
Replace Any with Generic:
// ❌ any loses type information
function identity(value: any): any {
return value;
}
// ✅ Generic preserves type
function identity<T>(value: T): T {
return value;
}
Replace Any with Union Type:
// ❌ any accepts anything
function format(value: any): string {
return String(value);
}
// ✅ Union type is specific
function format(value: string | number | boolean): string {
return String(value);
}
Replace Any with Type Predicate:
// ❌ any in type guard
function isString(value: any): boolean {
return typeof value === 'string';
}
// ✅ Type predicate with unknown
function isString(value: unknown): value is string {
return typeof value === 'string';
}
Return Type Annotations
ALWAYS add explicit return types (enforced by ESLint):
// ❌ Missing return type
function getUser(id: string) {
return db.query('SELECT * FROM users WHERE id = ?', [id]);
}
// ✅ Explicit return type
function getUser(id: string): Promise<User | null> {
return db.query('SELECT * FROM users WHERE id = ?', [id]);
}
// ✅ Void for no return
function logError(error: Error): void {
console.error('Error:', error.message);
}
// ✅ Never for functions that don't return
function throwError(message: string): never {
throw new Error(message);
}
Type Narrowing Patterns
Type Guards
// User-defined type guard
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value &&
typeof (value as User).id === 'string' &&
typeof (value as User).name === 'string'
);
}
// Usage
function processData(data: unknown): void {
if (isUser(data)) {
console.log(data.name); // data is User here
}
}
Discriminated Unions
// Result type with discriminated union
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function handleResult<T>(result: Result<T>): T {
if (result.success) {
return result.data; // TypeScript knows this is success branch
} else {
throw result.error; // TypeScript knows this is error branch
}
}
Assertion Functions
// Assert function (throws if condition fails)
function assertDefined<T>(
value: T | null | undefined,
message = 'Value is null or undefined',
): asserts value is T {
if (value === null || value === undefined) {
throw new Error(message);
}
}
// Usage
function process(user: User | null): void {
assertDefined(user, 'User not found');
console.log(user.name); // user is User here
}
Generic Constraints
// ❌ Unconstrained generic
function merge<T>(obj1: T, obj2: T): T {
return { ...obj1, ...obj2 }; // Error: Spread requires object
}
// ✅ Constrained to objects
function merge<T extends object>(obj1: T, obj2: T): T {
return { ...obj1, ...obj2 };
}
// ✅ More specific constraint
function merge<T extends Record<string, unknown>>(
obj1: T,
obj2: Partial<T>,
): T {
return { ...obj1, ...obj2 };
}
Workflow
Phase 1: Analysis
- Run
tsc --noEmitto find errors - Scan for
anytypes:grep -r ": any" src/ - Identify missing return types
- Prioritize by severity (compiler errors first)
Phase 2: Remediation
- Replace
anywithunknownor specific types - Add explicit return type annotations
- Fix null/undefined handling with guards
- Add type narrowing where needed
Phase 3: Validation
- Run
tsc --noEmit(must pass) - Run
npm run lint:ci(must pass) - Verify zero
anytypes remain - Check all functions have return types
Common Issues
Array index flagged as potentially undefined
- Solution: Check for undefined or use optional chaining
Object property access on potentially null/undefined
- Solution: Use optional chaining and nullish coalescing:
user?.profile?.name ?? 'Unknown'
Function parameters inferred as any
- Solution: Add explicit type annotations:
function process(data: { value: string })
Type assertion needed for external library
- Solution: Create type definition file in
types/directory
Best Practices
Prefer Unknown Over Any:
// ✅ unknown requires type narrowing
function parse(json: string): unknown {
return JSON.parse(json);
}
Use Type Assertions Sparingly:
// ⚠️ Type assertion (use only when certain)
const user = data as User;
// ✅ Type guard with runtime check
if (isUser(data)) {
const user = data; // Type narrowed safely
}
Document Complex Types:
/**
* Result type for async operations
* @typeParam T - Success value type
* @typeParam E - Error type (defaults to Error)
*/
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
Success Criteria
- Zero TypeScript compiler errors
- Zero ESLint type errors
- No
anytypes in production code - All functions have explicit return types
- Strict mode compliance: 100%
References
- TypeScript Strict Mode: https://www.typescriptlang.org/tsconfig#strict
- ESLint TypeScript Plugin: https://typescript-eslint.io/
- TypeScript Handbook: https://www.typescriptlang.org/docs/handbook/intro.html
Didn't find tool you were looking for?