Closed
Description
When trying to define a self type like this class Foo<Self extends Foo<Self>>
it is unclear if the methods of the class that return instances of itself should use Self
or Foo<Self>
as the return type.
- Using
Self
as the return type might cause issues when you need to use the typeFoo
. This will happen when you know only at runtime which subclass ofFoo
is used.
abstract class Foo<Self extends Foo<Self>> {
final bool property;
const Foo(this.property);
Self copyWith(bool property);
Self toggle() {
return copyWith(!property);
}
}
class Bar extends Foo<Bar> {
const Bar(super.property);
@override
Bar copyWith(bool property) {
return Bar(property);
}
}
void main() {
// Let's pretend that there are multiple subclass of `Foo`
// and that you know only at runtime which one you will get
// so that you need to use the type `Foo`
Foo someFoo = const Bar(true); // type: Foo<Foo<dynamic>>
someFoo = someFoo.toggle(); // type: Foo<dynamic>
}
The static analysis fails because when applying the toggle
method the type becomes Foo<dynamic>
instead of Foo<Foo<dynamic>>
.
- Using
Foo<Self>
as the return type doesn't mix well with using the name of the subclass as the type. In this example, it would preferable to be able to use the typeBar
for the variablesomeFoo
but this is not possible as thetoggle
method has a return typeFoo<Bar>
.
abstract class Foo<Self extends Foo<Self>> {
final bool property;
const Foo(this.property);
Foo<Self> copyWith(bool property);
Foo<Self> toggle() {
return copyWith(!property);
}
}
class Bar extends Foo<Bar> {
const Bar(super.property);
@override
Bar copyWith(bool property) {
return Bar(property);
}
}
void main() {
Bar someFoo = const Bar(true); // type: Bar
someFoo = someFoo.toggle(); // type: Foo<Bar>
}
The static analysis fails because someFoo.toggle()
uses the type Foo<Bar>
and the variable someFoo
is defined with type Bar
.
Related to dart-lang/language#3025