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.

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.

analysis_options.yaml
many_lints:
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.

- 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't
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
// Do
void 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.dart
Widget wrap(Widget child) => Scaffold(body: child); // LINT
// Do — an atom returns itself; the page decides the frame
Widget wrap(Widget child) => Padding(
padding: const EdgeInsets.all(8),
child: child,
);

The same Scaffold outside lib/design_system/atoms/** is untouched.

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.'

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.dart
void apply(OrderDto order) {} // LINT
// Do
void 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:

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.

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

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.