In Dart, when two imported extensions both define the same member for a type, how is the conflict resolved?
answer
- most specific on type wins
- neither more specific: error
- explicit application by name
- prefix when names clash too
- unnamed cannot be applied
basics
~20 sIf both extensions apply, Dart picks the one whose on type is more specific; if neither is, the implicit call is a compile-time ambiguity error. Fix it by applying one explicitly, NumberParsing('42').parseInt(), by limiting an import, or with an import prefix when the extension names also clash.
solid answer
~40 sWhen a member name is found in several applicable extensions, Dart chooses the most specific one, broadly the extension whose on type is a subtype of the other's, so `on String` beats `on Object`. If neither is more specific, the analyzer reports that the member is defined in both and neither is more specific. You then have three tools. Apply the extension explicitly, an **extension override**: `NumberParsing('42').parseInt()`. Limit what one import exposes (the import combinators belong to library structure). If the two extensions also share a name, import one with a prefix and write `rad.NumberParsing('42').parseInt()`. This is why exported extensions should be named: an unnamed extension cannot be applied explicitly and is visible only in its own library anyway.
code
dart · 19 lines// string_apis.dart
extension NumberParsing on String {
int parseInt() => int.parse(this);
}
// string_apis_2.dart
extension NumberParsing2 on String {
int parseInt() => int.parse(trim());
}
// main.dart
import 'string_apis.dart';
import 'string_apis_2.dart';
void main() {
// print('42'.parseInt()); // error: defined in both, neither is more specific
print(NumberParsing('42').parseInt()); // explicit extension override
print(NumberParsing2(' 42').parseInt()); // the other one
}go deeper
Recall that an extension can be applied explicitly by name, like NumberParsing('42').parseInt(), when two extensions clash.
Explain the most-specific rule, when it produces an ambiguity error, and the override, import and prefix fixes.
Prevent conflicts in shared packages with specific names and named extensions, and resolve third-party clashes at call sites without broad import changes.
Govern which shared types internal packages may extend so extension collisions do not become an integration cost across teams.
## How conflicts arise Extensions are resolved by the static type of the receiver, and any number of imported extensions may target the same type. A Flutter app might import two helper packages that both add `parseInt()` to `String`. Whenever a call like `'42'.parseInt()` finds more than one **applicable** extension, Dart must pick one or report an error. An extension is **applicable** when its on type matches the receiver's static type and the static type does not already have a real member of that name (instance members always win). ## Rule 1: the most specific extension wins If several extensions apply, Dart chooses the **most specific**. In the common case that means the one whose on type is a **subtype** of the other's: ```dart extension ShoutObject on Object { String shout() => 'object'; } extension ShoutString on String { String shout() => 'string'; } 'hi'.shout(); // 'string' — String is more specific than Object ``` For generic extensions the comparison uses the on types instantiated for the receiver, so `on Iterable<T>` loses to `on List<T>` for a list receiver. The full rule has one more tier: an extension declared in your own code is preferred over one declared in a `dart:` platform library. ## Rule 2: no winner means an error If two extensions apply and neither is more specific — typically two `on String` extensions from different packages — the implicit call does not compile. The analyzer's message (`ambiguous_extension_member_access`) reads: *A member named 'parseInt' is defined in 'NumberParsing' and 'NumberParsing2', and neither is more specific.* Dart never picks one silently based on import order. ## Tool 1: apply the extension explicitly An **extension override** names the extension and passes the receiver like a constructor argument: ```dart import 'string_apis.dart'; // NumberParsing import 'string_apis_2.dart'; // NumberParsing2 void main() { // print('42'.parseInt()); // error: ambiguous print(NumberParsing('42').parseInt()); print(NumberParsing2('42').parseInt()); } ``` Facts about overrides: - It looks like a wrapper object, but **nothing is allocated**; it is still a static call. - It takes **exactly one argument**, the receiver, which must be assignable to the on type. - It can only access **instance members**; static members are reached through the extension name, e.g. `NumberParsing.someStatic`. - It cannot be the receiver of a **cascade** (`..`), because an override has no value of its own. ## Tool 2: limit an import Restricting what one import exposes removes the second extension from scope, so the implicit call becomes unambiguous again. The import combinators themselves are part of library structure and are covered there. ## Tool 3: a prefix when extension names also clash If both libraries call their extension `NumberParsing`, an explicit override is ambiguous by name. Import one with a prefix: ```dart import 'string_apis.dart'; import 'string_apis_3.dart' as rad; NumberParsing('42').parseInt(); // from string_apis.dart rad.NumberParsing('42').parseInt(); // from string_apis_3.dart ``` dart.dev notes that extensions from a prefixed import still apply **implicitly**; the prefix is only needed when you name the extension explicitly. ## Why naming matters | Extension kind | Visible to importers | Can be applied explicitly | Use for | |---|---|---|---| | named (`extension NumberParsing on String`) | yes | yes | anything exported | | unnamed (`extension on String`) | no — only its own library | no | file-private helpers | An unnamed extension cannot cause a conflict in another library, since it is not visible there, but it also gives you no way to resolve a conflict involving it inside its own library. ## Practical guidance 1. Name every exported extension after what it does (`DateFormatting`, `JsonReading`). 2. Keep extension member names specific enough not to collide with other packages or future members of the type. 3. When a conflict appears, prefer an explicit override at the few call sites over reshaping imports for the whole file.
- In Dart, does an explicit extension override such as NumberParsing('42') allocate an object?No. It only tells the compiler which extension to use; the call is still a static function call with `'42'` as the receiver. That is also why an override cannot be stored in a variable or used as a cascade receiver.
- In Dart, if one extension is on String and another on String?, which is used for a non-null String receiver?The `on String` extension, because `String` is a subtype of `String?`, which makes it the more specific on type. For a receiver whose static type is `String?`, only the `String?` extension applies.
saying these in an interview costs you the question
- The extension imported last silently wins a conflict.
- Two extensions with the same member always cause a compile error.
- An explicit extension override creates a wrapper object at runtime.
- Unnamed extensions can be applied explicitly with an empty name.
- Prefixed imports disable implicit use of their extensions.