Teaches when and how to use unknown instead of any type in TypeScript. Use when working with TypeScript code that has any types, needs type safety, handling external data, or when designing APIs. Critical for preventing type safety violations.
Replaces unsafe `any` types with `unknown` and type guards. Activates when encountering `: any` patterns or working with external data to prevent runtime errors while maintaining flexibility.
/plugin marketplace add djankies/claude-configs/plugin install typescript@claude-configsThis skill is limited to using the following tools:
any type annotationsany: any, <any>, as any
</when-to-activate>
The unknown type is any's type-safe counterpart. It accepts any value like any, but requires type narrowing before use.
Key Pattern: any → unknown → type guard → safe access
Impact: Prevents runtime errors while maintaining flexibility for truly dynamic data. </overview>
<workflow> ## Decision FlowStep 1: Identify any Usage
When you see any, ask:
Step 2: Choose Replacement Strategy
Strategy A: Use Specific Types (preferred)
Strategy B: Use unknown (when truly dynamic)
Strategy C: Keep any (rare, justified cases)
Step 3: Implement Type Guards
When using unknown, implement type guards:
For detailed patterns and examples:
❌ Using any (unsafe)
async function fetchUser(id: string): Promise<any> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
const user = await fetchUser("123");
console.log(user.name.toUpperCase());
Problem: If API returns { username: string } instead of { name: string }, this crashes at runtime. TypeScript provides no protection.
✅ Using unknown + validation (safe)
async function fetchUser(id: string): Promise<unknown> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
function isUser(value: unknown): value is { name: string } {
return (
typeof value === "object" &&
value !== null &&
"name" in value &&
typeof value.name === "string"
);
}
const userData = await fetchUser("123");
if (isUser(userData)) {
console.log(userData.name.toUpperCase());
} else {
throw new Error("Invalid user data");
}
Better: Use Zod for complex validation (use the using-runtime-checks skill)
❌ Using any in generic default (unsafe)
interface ApiResponse<T = any> {
data: T;
status: number;
}
const response: ApiResponse = { data: "anything", status: 200 };
response.data.nonexistent.property;
Problem: Generic defaults to any, losing all type safety.
✅ Using unknown default (safe)
interface ApiResponse<T = unknown> {
data: T;
status: number;
}
const response: ApiResponse = { data: "anything", status: 200 };
if (typeof response.data === "string") {
console.log(response.data.toUpperCase());
}
Even Better: Require explicit type parameter
interface ApiResponse<T> {
data: T;
status: number;
}
const response: ApiResponse<User> = await fetchUser();
❌ Using any for caught errors (unsafe)
try {
await riskyOperation();
} catch (error: any) {
console.log(error.message);
}
Problem: Not all thrown values are Error objects. This crashes if someone throws a string or number.
✅ Using unknown + type guard (safe)
try {
await riskyOperation();
} catch (error: unknown) {
if (error instanceof Error) {
console.log(error.message);
} else {
console.log("Unknown error:", String(error));
}
}
❌ Using any parameter (unsafe)
function validate(data: any): boolean {
return data.email && data.password;
}
Problem: Typos like data.emial are not caught. No autocomplete support.
✅ Using unknown + type guard (safe)
interface LoginData {
email: string;
password: string;
}
function isLoginData(data: unknown): data is LoginData {
return (
typeof data === "object" &&
data !== null &&
"email" in data &&
"password" in data &&
typeof data.email === "string" &&
typeof data.password === "string"
);
}
function validate(data: unknown): data is LoginData {
if (!isLoginData(data)) {
return false;
}
return data.email.includes("@") && data.password.length >= 8;
}
</examples>
<constraints>
**MUST:**
unknown for external data (APIs, JSON.parse, user input)unknown for generic defaults when type is truly dynamicunknown valuesSHOULD:
unknown when structure is knownvalue is Type) for reusable type guardsunknown progressively (check object → check properties → check types)NEVER:
any for external dataas any to silence TypeScript errorsany in public API surfacesanyunknown to specific types without validation
</constraints>
After replacing any with unknown:
Type Guard Exists:
unknown value has a corresponding type guardSafe Access Only:
Error Handling:
Compilation:
npx tsc --noEmit
Should pass without any suppressions.
1. Library Interop (temporary)
import poorlyTypedLib from "poorly-typed-lib";
const result = poorlyTypedLib.method() as any;
Consider contributing types to DefinitelyTyped or wrapping in typed facade.
2. Type Manipulation Edge Cases
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
Sometimes TypeScript's type system can't express complex patterns. Document why.
3. Explicit Opt-Out
const configSchema: any = generateFromSpec();
Explicitly choosing to skip type checking for this value. Document decision. </common-patterns>
<migration-path> ## Migrating Existing `any` UsagePhase 1: Audit
grep -rn ": any" src/
grep -rn "<any>" src/
grep -rn "= any" src/
Phase 2: Classify
unknown + validationunknownany → document with comment explaining whyPhase 3: Replace
Phase 4: Prevent
noImplicitAny in tsconfig.jsonanyany introduction
</migration-path>
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.