The component compiles. That’s because the agent wrote props as any, or value as string, or sprinkled // @ts-expect-error on a <Select> that now takes a number. The runtime still blows up when the form submits "".

I’ve started treating new any / as on UI props as a merge blocker, not a typing nit. The cast is often hiding the actual bug the PR was supposed to fix.

Where the casts show up

Typical patterns in agent diffs:

  • ...(props as any) to spread into a design-system component
  • onChange={(e: any) => ...} because the event type didn’t match
  • items as Foo[] after a filter that TypeScript correctly widened
  • <Button {...(rest as ButtonProps)}> when rest still contains a href
  • as unknown as Props — two casts, still one lie

Agents do this when the types are right and the call site is wrong, or when a union wasn’t narrowed. A cast makes the screenshot possible. It does not make the state valid.

A simple policy

  • No new any, as any, or as unknown as on component props, event handlers, or JSX spreads in feature UI
  • as is allowed only for a documented boundary (a JSON parse you already validate, a DOM API, a test fixture) and must sit next to that validation
  • @ts-expect-error needs a one-line reason and a follow-up; it is not a way to land a Select
  • If the system component’s types are wrong, fix or wrap that type in a named helper — don’t punch through at every call site

Existing casts in untouched lines can stay until you edit that component. New ones in the diff should justify themselves.

What to put in the prompt

Do not use `any`, `as any`, `as unknown as`, or empty @ts-expect-error to make this UI compile. If props don’t match, change the component’s prop type or the caller. If you must assert, name the type and explain the runtime check that makes it safe. Prefer narrowing (typeof, in, discriminated unions) over casts. 

Paste the existing prop type and one correct call site. The agent is much less likely to invent as any when it can see a working example.

How to catch it in review

git diff -U0 -- '*.ts' '*.tsx' | rg -n "\bas any\b|as unknown as|: any\b|@ts-expect-error|@ts-ignore" 

For each hit in the new hunks:

  1. Is there a runtime check next to it?
  2. Could a narrowed type or a better prop definition remove it?
  3. If it’s on a JSX spread, what extra keys might leak through?

CI can fail on new : any / as any in src/ (a simple diff grep, or @typescript-eslint/no-explicit-any on changed files). Use that if the team already agrees. Review still catches as SomeType that is merely hopeful.

Reject / send back

Reject

  • as any / as unknown as on props to “just get it compiling”
  • Event handlers typed as any to skip ChangeEvent
  • Spreading an object into a component through a cast because it has extra fields
  • A new // @ts-expect-error with no reason

Accept

  • A tightened prop type, or a caller that now passes the right union member
  • A named, validated boundary (parseResult.data after Zod / Valibot)
  • An existing cast you didn’t touch, called out if you’re aware of it

Tiny habit

When the agent’s UI PR “compiles now,” search the diff for any and as. If a cast appeared to paper over a prop mismatch, send it back and ask for a real type fix. The compiler was trying to help. Don’t disable it in the JSX.