avoid_banned_types
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.
Deprecation annotations tell you a type is going away; they do not stop new code from reaching for it. A lint turns “remaining usages” into a number CI reports instead of a grep somebody remembers to run.
This rule is in no preset and reports nothing until you add banned:. Every example below shows the configuration that produces it.
many_lints: rules: avoid_banned_types: banned: - deny: ['LegacyUser'] message: 'Use User instead; LegacyUser is removed in v3.'rules: avoid_banned_types: banned: - deny: ['LegacyUser'] message: 'Use User instead; LegacyUser is removed in v3.'deny takes a bare Name or a qualified package:uri#Name. Add in: to limit an entry to part of the tree.
Examples
Section titled “Examples”Retire a deprecated model
Section titled “Retire a deprecated model”- deny: ['LegacyUser'] message: 'Use User instead; LegacyUser is removed in v3.'Every written mention of the type is reported — parameter types, return types, type arguments, and constructor calls:
// Don'tvoid greet(LegacyUser user) {} // LINT: parameter typeLegacyUser find() => LegacyUser('a'); // LINT: return type, and the callList<LegacyUser> all = []; // LINT: as a type argument
// Dovoid greet(User user) {}User find() => const User('ada');List<User> all = [];An import prefix does not hide a usage: matching is on the type’s declared
name, so p.LegacyUser still matches a LegacyUser entry.
Keep a layout widget out of the atoms layer
Section titled “Keep a layout widget out of the atoms layer”- deny: ['Scaffold'] in: ['lib/design_system/atoms/**'] message: 'Atoms must not depend on page-level layout.'// Don't — in lib/design_system/atoms/primary_button.dartWidget wrap(Widget child) => Scaffold(body: child); // LINT
// Do — an atom returns itself; the page decides the frameWidget wrap(Widget child) => Padding( padding: const EdgeInsets.all(8), child: child,);The same Scaffold outside lib/design_system/atoms/** is untouched.
Disambiguate a common name
Section titled “Disambiguate a common name”Banning Border outright would also catch a local class of that name. Qualify
with package:uri#Name to ban only one library’s:
- deny: ['package:flutter/src/painting/border.dart#Border'] message: 'Use the design-system Border token.'Ban a whole family with a pattern
Section titled “Ban a whole family with a pattern”deny_pattern is anchored to the whole name:
- deny_pattern: ['.*Dto'] in: ['lib/domain/**'] message: 'Transport types belong in lib/data; the domain holds entities.'// Don't — in lib/domain/checkout.dartvoid apply(OrderDto order) {} // LINT
// Dovoid apply(Order order) {}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:
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.'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.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
banned |
list of entries | [] |
The entries to enforce. With none, the rule reports nothing |
Per entry:
| 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 |
Known limitations
Section titled “Known limitations”Written types only. The rule fires where a type is named. A value whose type is only inferred — final user = find(); returning a banned type — is not reported, because there is nothing on the line to point at.
Bare names are ambiguous. A bare deny: ['State'] matches every type named State from any library. Use package:uri#Name when the name is not yours alone.
A malformed entry is skipped silently. An invalid regular expression costs you that pattern and nothing else — a plugin cannot report problems against a YAML file.
Related rules
Section titled “Related rules”avoid_banned_annotations— Ban specific annotations, optionally scoped by directory.avoid_banned_exports— Ban re-exports of specific libraries, optionally scoped by directory.avoid_banned_imports— Ban imports of specific libraries, optionally scoped by directory.avoid_banned_names— Ban specific identifiers from being used as declaration names.