Skip to content

avoid_collection_methods_with_unrelated_types

v0.3.0WarningConfigurableCollection & Type

Flags contains, remove, lookup, containsKey, containsValue and map indexing called with an argument whose type is unrelated to the collection’s element or key type. These methods take Object?, so the compiler accepts the call — and it silently answers false, null or nothing at all.

The usual cause is an id whose type changed. The lookup compiles, returns false forever, and the feature quietly stops working:

bool isSelected(List<int> selectedIds, String id) {
return selectedIds.contains(id);
}

Convert at the boundary so the types line up:

bool isSelected(List<int> selectedIds, String id) {
final parsed = int.tryParse(id);
return parsed != null && selectedIds.contains(parsed);
}

containsKey and remove are checked against the key type, containsValue against the value type:

// Don't
final labels = <int, String>{1: 'One'};
labels.containsKey('1'); // key type is int
labels.containsValue(42); // value type is String
labels.remove('1');
// Do
final labels = <int, String>{1: 'One'};
labels.containsKey(1);
labels.containsValue('One');
labels.remove(1);

Indexing a map is checked the same way, which catches the commonest form of the mistake:

// Don't
final label = labels['1']; // always null
// Do
final label = labels[1];

Relatedness runs in both directions, so a subtype argument is accepted:

final measurements = <num>[1, 2, 3];
measurements.contains(42); // int is a num — accepted
measurements.contains(1.5); // double is a num — accepted

A dynamic argument is not reported by default — its runtime value may well be the right type. Turn strict on in a codebase that has eliminated dynamic and wants the stragglers surfaced:

many_lints.yaml
rules:
avoid_collection_methods_with_unrelated_types:
strict: true
final ids = <int>[1, 2, 3];
dynamic raw = readFromCache();
// Reported only with strict: true
ids.contains(raw);
// Do — narrow before the lookup
if (raw is int) {
ids.contains(raw);
}

Type parameters are never reported, in strict mode or not: List<T>.contains(value) cannot be judged until T is chosen at the call site.

A dynamic collection tells you nothing. List<dynamic>.contains(anything) is always accepted, since the element type carries no constraint to violate.

Two unrelated classes are compared by hierarchy only. Neither being a subtype of the other is enough to report — unlike avoid_unrelated_type_casts, this rule does not first ask whether a third class could implement both.

This rule is in the core preset, so it is on with preset: core, preset: recommended or preset: opinionated.

To turn it off:

many_lints.yaml
rules:
avoid_collection_methods_with_unrelated_types: false

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

analysis_options.yaml
many_lints:
rules:
avoid_collection_methods_with_unrelated_types:
strict: true
Option Type Default Description
strict bool false Also report a dynamic argument passed where a known element type is expected