---
name: avoid-type-assertions
description: Use when writing or reviewing TypeScript code that uses `as` type assertions. Triggers on any `as SomeType` cast, type widening, or situations where you're tempted to bypass TypeScript's type checker. Always use this skill before reaching for `as` — type annotations and `satisfies` are almost always better.
---

# Avoid Type Assertions

## Overview

Never use `as` type assertions to bypass TypeScript's type checker. Use type annotations (`: Type`) or the `satisfies` operator instead — they provide the same convenience with actual compile-time safety.

## The Problem with `as`

```typescript
// ❌ Bypasses type checking — TS trusts you blindly
const config = { apiUrl: 'https://api.example.com', timeout: 5000 } as ApiConfig;
// Missing required fields? TS won't tell you.
```

`as` tells TypeScript "trust me, I know the type." Type annotations tell TypeScript "verify this for me."

## The Three Alternatives

### 1. Type Annotation — default choice

Use when you want the variable typed as an interface and you want TS to verify conformance:

```typescript
// ✅ TS checks all required fields are present
const config: ApiConfig = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3
};
```

### 2. `satisfies` — when literal types matter

Use when you need both conformance checking AND preservation of exact literal types:

```typescript
// ✅ Checks Record<string, Handler> shape, but keeps exact key literals
const routeHandlers = {
  '/users': handleUsers,
  '/posts': handlePosts,
} satisfies Record<string, RequestHandler>;
// Type: { '/users': typeof handleUsers, '/posts': typeof handlePosts }
// Not: Record<string, RequestHandler>
```

### 3. Type Guards — for runtime checks

Use instead of `as` when narrowing from `unknown` or external data:

```typescript
// ❌ No runtime safety
const user = apiResponse as User;

// ✅ Runtime-verified narrowing
function isUser(obj: unknown): obj is User {
  return typeof obj === 'object' && obj !== null && 'id' in obj;
}
if (isUser(apiResponse)) { /* safely typed */ }
```

## Decision Guide

| Situation | Use |
|---|---|
| Typed variable matching an interface | `: Type` annotation |
| Preserving literal types in objects/arrays | `satisfies Type` |
| Narrowing from `unknown` / external data | type guard |
| DOM element with known specific type | `instanceof` check |
| Third-party lib with bad types (last resort) | `as Type` with comment |

## When `as` Is Acceptable

- DOM queries where `instanceof` is impractical and you're certain: `el as HTMLInputElement`
- Third-party libraries with no `@types` package — document why
- Double assertion for genuinely complex narrowing TypeScript can't infer

Always add a comment when using `as` explaining why the alternatives don't apply.

## Common Mistakes

```typescript
// ❌ Asserting a return value instead of annotating the function
function getUser() { return { id: 1 } as User; }

// ✅ Annotate the return type
function getUser(): User { return { id: 1 }; }

// ❌ Using as to widen an incompatible type
const x = "hello" as unknown as number; // double assertion = red flag

// ✅ Fix the underlying type mismatch
```

## Enforcement

Enable `@typescript-eslint/consistent-type-assertions` with `assertionStyle: "never"` (allowing DOM `instanceof` exceptions). When you encounter `as` in existing code during a review, flag it unless it falls under the acceptable cases above.
