avoid_unrelated_type_casts
Flags an as cast or is check between types with no possible common subtype. The cast can only ever throw; the check can only ever be false.
The analyzer accepts value as int on a String without complaint — the cast is legal Dart, it simply fails at runtime. The is form is quieter and worse: a check that is statically always false compiles, runs, and skips its branch forever, leaving dead code that looks live.
The SDK’s unrelated_type_equality_checks covers the == version of this idea; casts and type tests are the gap this rule fills.
The usual cause is a type that changed under the cast — an id that became a String, a field that was widened — leaving a cast nothing can satisfy:
int parseCount(String raw) { return raw as int;}Convert, rather than assert, when the types are genuinely different:
int parseCount(String raw) { return int.parse(raw);}An is check that can never be true
Section titled “An is check that can never be true”// Don't — the branch is dead codevoid report(String message) { if (message is int) { log('numeric: $message'); }}// Do — `Object` really might be an `int`void report(Object message) { if (message is int) { log('numeric: $message'); }}Limiting the rule to as casts
Section titled “Limiting the rule to as casts”Some codebases narrow a sealed hierarchy in ways that read as unrelated to a purely syntactic check. Turn off the is half and keep the casts:
rules: avoid_unrelated_type_casts: report_is_checks: false// Still reportedfinal n = someString as int;
// No longer reportedif (someString is int) { }Known limitations
Section titled “Known limitations”The rule is deliberately conservative about what counts as “unrelated”. Two ordinary classes are not reported, because a third class could implement both, making the cast unusual rather than impossible:
class Foo {}class Bar {}
class Both implements Foo, Bar {}
Bar f(Foo value) => value as Bar; // legal: `value` might be a `Both`Reports are limited to cases where no such subtype can exist: final and sealed classes, enums, and dart:core types like String and int.
dynamic, Object and void are compatible with everything by design, so a cast from them is the normal way to narrow an untyped value and is never reported. Type parameters are skipped too — the real type argument is unknown, so any conclusion would be guesswork. Nullability alone is not a relation difference: String? to String is the analyzer’s business, not this rule’s.
Turning this rule off
Section titled “Turning this rule off”This rule is in the core preset, so it is on with preset: core,
preset: recommended or preset: opinionated.
To turn it off:
rules: avoid_unrelated_type_casts: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Options
Section titled “Options”many_lints: rules: avoid_unrelated_type_casts: report_is_checks: falserules: avoid_unrelated_type_casts: report_is_checks: false| Option | Type | Default | Description |
|---|---|---|---|
report_is_checks |
bool | true |
Also report is checks. Set to false to limit the rule to as casts |
Related rules
Section titled “Related rules”avoid_collection_methods_with_unrelated_types— Avoid calling collection methods with arguments whose types are unrelated to the collection’s type parameter.prefer_correct_json_casts— Cast JSON values to nullable types.avoid_accessing_collections_by_constant_index— Avoid accessing a collection by a constant index inside a loop.avoid_collection_equality_checks— Avoid comparing collections with == or != as it checks reference equality, not contents.