Skip to content

avoid_banned_types

v1.0.0WarningConfigurableArchitecture

Flags any mention of a type you ban, optionally only inside the directories you name. Useful for retiring a deprecated model, keeping a platform type out of shared code, or confining a design-system widget to the layer that owns it.

This rule reports nothing until you configure it.

Deprecation annotations tell you a type is going away; they do not stop new code from reaching for it. During a migration the count of remaining usages is the thing you actually want to drive to zero, and a lint turns that into a number your CI reports instead of a grep somebody remembers to run.

The second use is layering. A design system where atoms must not depend on page-level layout has no way to express that in the type system — Scaffold is importable from anywhere. Banning it inside lib/design_system/atoms/** states the constraint where it can be checked.

See also: Effective Dart: deprecation, Atomic Design

// With an entry banning 'LegacyUser':
void greet(LegacyUser user) {} // LINT: parameter type
LegacyUser find() => LegacyUser('a'); // LINT: return type, and the call
List<LegacyUser> all = []; // LINT: as a type argument
void greet(User user) {}
User find() => const User('ada');
List<User> all = [];

This rule is in no preset, so it is off unless you enable it by name:

many_lints.yaml
rules:
avoid_banned_types: true

To turn it off again:

many_lints.yaml
rules:
avoid_banned_types: false

To keep the rule on but skip certain paths, use per-rule exclude.

analysis_options.yaml
many_lints:
rules:
avoid_banned_types:
banned:
- deny: ['LegacyUser']
message: 'Use User instead; LegacyUser is removed in v3.'
- deny: ['Scaffold']
in: ['lib/design_system/atoms/**']
message: 'Atoms must not depend on page-level layout.'
Option Type Default Description
banned list of maps [] The entries to enforce. With none, the rule reports nothing

Each entry accepts:

Key Type Required Description
deny string or list one of deny / deny_pattern Type names banned by exact match. Accepts a bare Name or a qualified package:uri#Name
deny_pattern string or list one of deny / deny_pattern Regular expressions, anchored to the whole name
in list of globs no Paths, relative to the package root, where the entry applies. Omit to apply everywhere
message string no A project-specific explanation appended to the diagnostic

Matching is on the type’s declared name rather than the name as written, so an import prefix cannot hide a usage — p.LegacyUser matches a LegacyUser entry.

Qualify with package:uri#Name when a bare name is ambiguous. Banning Border outright would also catch a local class of that name; package:flutter/src/painting/border.dart#Border bans only Flutter’s.

Recipe: keeping heavy FP machinery out of an fpdart codebase

Section titled “Recipe: keeping heavy FP machinery out of an fpdart codebase”

A common house rule is that a project uses fpdart’s Option / Either / TaskEither but deliberately not Reader, State or the IO* family — dependency injection goes through the DI mechanism the app already has, and plain sync code covers the rest. This rule enforces that without needing one of its own:

analysis_options.yaml
many_lints:
rules:
avoid_banned_types:
banned:
- deny:
- 'package:fpdart/src/reader.dart#Reader'
- 'package:fpdart/src/state.dart#State'
- 'package:fpdart/src/io_option.dart#IOOption'
message: 'Use Riverpod for injection and plain Dart for sync code.'

Qualify these with the package:uri#Name form. State in particular is a name Flutter also uses, and a bare entry would ban every State subclass in the app.