chainedThis assertion chain discards type evidence. Keep the original precise type, or parse untrusted input at its boundary before narrowing it.small-rules/no-chained-type-assertionsDisallow chained TypeScript as and angle-bracket assertions, including parenthesized chains.
Ported from dmmulroy/anti-slop’s
no-chained-type-assertions — I wanted the same guard against casts that fabricate evidence.
Credit for the rule idea is his; the port here just matches my checker and my one carve-out.
Fabricated evidence: the second as (or angle-bracket) doesn’t narrow anything — it just
discards whatever the first one claimed. value as string as number proves neither.
Boundary parsing over brute-forcing: when you see as unknown as T or
as any as T the input is probably untrusted. I’d rather you validate it at the boundary
with a guard or schema and keep the precise type from there. Almost never worth papering over.
Parenthesized chains count: (value as string) as number is the same trick with parens.
The rule unwraps them, so you can’t hide it.
Where I differ is I allow as const as const. It’s idempotent — it doesn’t widen or narrow, so
there’s nothing to discard. I also let you name specific identifier targets for a single
as unknown as T bridge via allowedTargets. That’s for nominal twins like Roblox
vector / Vector3, not a free pass: as unknown as string still flags.
Everything else with two non-const assertions is a problem.
chainedThis assertion chain discards type evidence. Keep the original precise type, or parse untrusted input at its boundary before narrowing it.This rule accepts one options object after the severity.
const x = value as string as number;const x = value satisfies string;