Claude Cursor opencode Skill

no-bare-casts

Writing `as` in TypeScript or TSX production code, modifying a file that contains a bare `as` cast, silencing a type error with a cast, encountering `as unknown as`, or reviewing a cast site.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download modem-dev-ossrules-public_files_prisma_skills-contrib_no-bare-casts-d2b6775.zip · 1 KB
Part of modem-dev/ossrules — 39 skills

Install

skills CLI npx skills add https://github.com/modem-dev/ossrules/tree/main/public/files/prisma/skills-contrib/no-bare-casts
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install modem-dev-ossrules@llmmart
Git git clone https://github.com/modem-dev/ossrules.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole modem-dev/ossrules collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

No bare as casts

Any bare as in production TypeScript is a signal to stop and work through the decision tree below. Test files (*.test.ts, *.test-d.ts, test/**/*.ts) are exempt — tests use as for stubbing and type assertions and that's fine.

Decision tree

Work through these in order before writing or keeping a cast:

  1. Tighten the input type. Can the parameter, generic bound, or return type at the source be made more specific so the cast is unnecessary?
  2. Add a runtime check. Can a type predicate (function isUser(x): x is User) narrow the type at runtime, eliminating the cast?
  3. Restructure a generic. Can a bound or constraint carry the needed information, making the cast unnecessary?
  4. Use satisfies. expr satisfies T checks the type without coercing it and is unaffected by this rule. Prefer it when you want a type-check, not a coercion.
  5. Use castAs<T>(value). When the value already satisfies T and the assertion is purely declarative, castAs is the right form.
  6. Only if none of the above: use blindCast<T, "Reason">(value). The Reason literal must name the specific compromise in language a reviewer can evaluate.

Import

import { blindCast, castAs } from '@internal/utils/casts';

Helper signatures

// Escape hatch — the value is genuinely opaque or unrelated to the target type.
// The Reason literal documents the compromise; the reviewer evaluates it.
function blindCast<TargetType, Reason extends string>(input: unknown): TargetType

// Declarative assertion — the value already satisfies T at runtime.
function castAs<T>(value: T): T

The Reason bar

blindCast is the auditable escape hatch of last resort — not a convenience wrapper. Reach for it only after the decision tree above has been exhausted. The second type argument must be a string literal that a reviewer can act on:

// ✅  Names the specific constraint
blindCast<User, "deserialized from contract validator; shape has already been checked">(raw)

// ❌  Adds no information — reviewer has nothing to evaluate
blindCast<User, "trust me">(raw)

A vague reason is the reviewer's signal to push back and the author's signal to revisit the type design.

"Convert when you touch"

When you touch a file that contains a bare as cast — even as part of unrelated work — convert it to one of the accepted forms or eliminate it. The CI ratchet (pnpm lint:casts) rejects per-PR cast-count increases; converting on contact is how the total comes down over time.

Files (ossrules)
  • SKILL.md 2.8 KB
    ---
    name: no-bare-casts
    description: >-
      Writing `as` in TypeScript or TSX production code, modifying a file that
      contains a bare `as` cast, silencing a type error with a cast,
      encountering `as unknown as`, or reviewing a cast site.
    ---
    
    # No bare `as` casts
    
    Any bare `as` in production TypeScript is a signal to stop and work through the decision tree below. Test files (`*.test.ts`, `*.test-d.ts`, `test/**/*.ts`) are exempt — tests use `as` for stubbing and type assertions and that's fine.
    
    ## Decision tree
    
    Work through these in order before writing or keeping a cast:
    
    1. **Tighten the input type.** Can the parameter, generic bound, or return type at the source be made more specific so the cast is unnecessary?
    2. **Add a runtime check.** Can a type predicate (`function isUser(x): x is User`) narrow the type at runtime, eliminating the cast?
    3. **Restructure a generic.** Can a bound or constraint carry the needed information, making the cast unnecessary?
    4. **Use `satisfies`.** `expr satisfies T` checks the type without coercing it and is unaffected by this rule. Prefer it when you want a type-check, not a coercion.
    5. **Use `castAs<T>(value)`.** When the value already satisfies `T` and the assertion is purely declarative, `castAs` is the right form.
    6. **Only if none of the above: use `blindCast<T, "Reason">(value)`.** The `Reason` literal must name the specific compromise in language a reviewer can evaluate.
    
    ## Import
    
    ```typescript
    import { blindCast, castAs } from '@internal/utils/casts';
    ```
    
    ## Helper signatures
    
    ```typescript
    // Escape hatch — the value is genuinely opaque or unrelated to the target type.
    // The Reason literal documents the compromise; the reviewer evaluates it.
    function blindCast<TargetType, Reason extends string>(input: unknown): TargetType
    
    // Declarative assertion — the value already satisfies T at runtime.
    function castAs<T>(value: T): T
    ```
    
    ## The `Reason` bar
    
    `blindCast` is the auditable escape hatch of last resort — not a convenience wrapper. Reach for it only after the decision tree above has been exhausted. The second type argument must be a string literal that a reviewer can act on:
    
    ```typescript
    // ✅  Names the specific constraint
    blindCast<User, "deserialized from contract validator; shape has already been checked">(raw)
    
    // ❌  Adds no information — reviewer has nothing to evaluate
    blindCast<User, "trust me">(raw)
    ```
    
    A vague reason is the reviewer's signal to push back and the author's signal to revisit the type design.
    
    ## "Convert when you touch"
    
    When you touch a file that contains a bare `as` cast — even as part of unrelated work — convert it to one of the accepted forms or eliminate it. The CI ratchet (`pnpm lint:casts`) rejects per-PR cast-count increases; converting on contact is how the total comes down over time.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related