Why can't Dart infer types in redirecting constructor? - dart

Minimum reproducible code:
class Parent {}
class Child extends Parent {}
class Foo<T extends Parent> {
final T t;
Foo(this.t);
Foo.one(Child child) : this(child); // Compile error
}
The argument type 'Child' can't be assigned to the parameter type 'T'.
Why can't I pass Child in redirecting constructor, in other words, why doesn't Dart know that Child satisfies T extends Parent relationship? Though I can do
void main() => Foo(Child());
Note: I'm looking for a reason as to why I can't do that. Please don't post answers to use this(child as T).

Because the code is not valid since you could do:
class Monster extends Parent {}
void main() {
final foo = Foo<Monster>.one(Child());
}
Which is a problem since Monster does extend from Parent but the constructor argument takes a Child and will then try assign this to T which is Monster.
If we do the change you don't want:
Foo.one(Child child) : this(child as T);
Then we will get the error:
type 'Child' is not a subtype of type 'Monster' in type cast
#0 new Foo.one (./bin/example1.dart:8:37)
#1 main (./bin/example1.dart:14:15)
#2 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#3 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:26)
So you cannot say that Child can always be assigned to a variable T where T extends Parent.

Related

How to use covariant in constructors?

Minimum reproducible code:
class Foo {}
class Bar extends Foo {}
class Baz {
final void Function(Foo) f;
Baz._(this.f);
Baz.one(void Function(Bar) func) : this._(func); // Compile error
}
I am getting this error:
The argument type 'void Function(Bar)' can't be assigned to the parameter type 'void Function(Foo)'
How can I use the covariant keyword to tell analyzer that a call to f will return Bar in my particular case?
Note: I can use this._(func as void Function(Foo)) to make it compile, but I'm looking for a better solution.

How to use type parameter when using redirecting constructor?

Minimum reproducible code:
class Foo<T extends num> {
final T t;
Foo._(this.t);
Foo.zero() : this._<int>(0); // Error
}
'' isn't a field in the enclosing class.
Note: I want to do it without casting, i.e. this._(0 as T) works but I want to use type argument int.

Restrictions on Type in dart

So, basically I need to create restrictions of which types can be used in a Type variable, something like this:
class ElementFilter<T extends Element> {
final Type<T> elementType; // What I want is something like Type<T>, but Type does not have a generic parameter
ElementFilter(this.elementType);
}
List<T> filterElements<T extends Element>(ElementFilter<T> element) {
return elements.where((el) => _isOfType(el, element.type)).toList();
}
filterElements(ElementFilter(ClassThatExtendsElement)); // Would work fine
filterELements(ElementFilter(String)); // Error, String does not extends Element
So it would only be possible to create ElementFilters with types that extend Element. Is this possible in some way?
I think you probably want:
/// Example usage: ElementFilter<ClassThatExtendsElement>();
class ElementFilter<T extends Element> {
final Type elementType;
ElementFilter() : elementType = T;
}
Unfortunately, there's no way to make the generic type argument non-optional. You will have to choose between having a required argument and having a compile-time constraint on the Type argument.
Dart doesn't support algebraic types, so if you additionally want to support a finite set of types that don't derive from Element, you could make specialized derived classes and require that clients use those instead of ElementFilter. For example:
class StringElementFilter extends ElementFilter<Element> {
#override
final Type elementType = String;
}
(You also could create a StringElement class that extends Element if you want, but at least for this example, it would serve no purpose.)
I highly recommend not using Type objects at all. Ever. They're pretty useless, and if you have the type available as a type parameter, you're always better off. (The type variable can always be converted to a Type object, but it can also be actually useful in many other ways).
Example:
class ElementFilter<T extends Element> {
bool test(Object? element) => element is T;
Iterable<T> filterElements(Iterable<Object?> elements) =>
elements.whereType<T>();
}
List<T> filterElements<T extends Element>(ElementFilter<T> filter) =>
filter.filterElements(elements).toList();
filterElements(ElementFilter<ClassThatExtendsElement>()); // Would work fine
filterElements(ElementFilter<String>()); // Error, String does not extends Element

Type Erasure with dart LinkedList?

This is the program:
import 'dart:collection';
class MyLinkedListEntry<T> extends LinkedListEntry<MyLinkedListEntry> {
T value;
MyLinkedListEntry(T this.value);
#override
String toString() => '${super.toString()}: ${value}';
}
void main(List<String> args) {
var l = LinkedList<MyLinkedListEntry>();
var s = MyLinkedListEntry("SomeString");
var p = MyLinkedListEntry(125);
l.add(s);
s.insertAfter(p);
p.insertAfter(MyLinkedListEntry(126));
l.forEach((e) => print(e));
}
And it gives this output:
Instance of 'MyLinkedListEntry<String>': SomeString
Instance of 'MyLinkedListEntry<int>': 125
Instance of 'MyLinkedListEntry<dynamic>': 126
I expected the third instance to be of type LinkedList<int> as well. Why it's not?
This is with Dart 2.13.4.
0. dynamic in type checking
Everything is a subclass of dynamic:
print(1 is dynamic); // Outputs true
print("a" is dynamic); // Outputs true
In fact, Dart even shows a warning when using the above code: Unnecessary type check; the result is always 'true'.
1. Omiting type parameters in declarations
In the declaration
class MyLinkedListEntry<T> extends LinkedListEntry<MyLinkedListEntry>
note that you're not passing the type parameter of MyLinkedListEntry in the type parameter of LinkedListEntry. From docs (emphasis mine):
When a generic class is instantiated without explicit type arguments, each type parameter defaults to its type bound [...] if one is explicitly given, or dynamic otherwise.
So Dart interprets this as
class MyLinkedListEntry<T> extends LinkedListEntry<MyLinkedListEntry<dynamic>>
2. The extends clause in type parameters
Let's look at the declaration of LinkedListEntry:
abstract class LinkedListEntry<E extends LinkedListEntry<E>>
Note that LinkedListEntry requires a type parameter named E, which must be a subclass of LinkedListEntry. When you use LinkedListEntry<E>, E must extend LinkedListEntry<E>.
When you declare MyLinkedListEntry<T>, you're passing MyLinkedListEntry<dynamic> as E. Since T always extends from dynamic, MyLinkedListEntry<T> extends LinkedListEntry<MyLinkedListEntry<dynamic>>, so this is a valid declaration.
3. Type parameters in methods
In the expression
p.insertAfter(MyLinkedListEntry(126));
you're using the insertAfter method declared in the LinkedListEntry class. Let's look at its declaration:
void insertAfter(E entry)
Since E is equal to MyLinkedListEntry<dynamic>, Dart will interpret any MyLinkedListEntry call to this method as
void insertAfter(MyLinkedListEntry<dynamic> entry)
Therefore, when you do
p.insertAfter(MyLinkedListEntry(126));
you're actually passing an upcasted MyLinkedListEntry<dynamic>, which explains the output.
The solution
Explicitly pass the type parameter of MyLinkedListEntry when inserting:
p.insertAfter(MyLinkedListEntry<int>(126));

Dart Abstract class of Generic Type with Named Constructor

I am attempting to construct an abstract class that requires a named constructor in Dart. Given some Map (m), this generic type must be able instantiate itself.
The Dart compiler is throwing T.fromJson -> Invalid constructor name.
My attempt at coding this:
abstract class JsonMap<T> {
Map toJson();
T.fromJson(Map m);
}
I struggled with the same concept (in the same place ... API parsing :)) ) and I didn't found a proper solution.
But maybe you can use something this thing I found while checking block pattern this (I am not using it for my model part):
abstract class SomeBase {
void load();
}
class Provider<T extends SomeBase> extends InheritedWidget {
final T something;
Provider({
Key key,
#required this.something,
}): super(key: key);
#override
bool updateShouldNotify(_) {
return true;
}
static Type _typeOf<T>() => T;
static T of<T extends SomeBase>(BuildContext context){
final type = _typeOf<Provider<T>>();
Provider<T> provider = context.inheritFromWidgetOfExactType(type);
return provider.something;
}
}
OR just use this without encapsulating it in an inherited widget and provide the already initialised objects (like user or whatever you are parsing) that just load the values from the JSON provided.
You're creating a class named JsonMap that is parameterized on type T. T is not the name of your class, so T.fromJson is not a valid named constructor for JsonMap.
If you want JsonMap to have a named constructor, it should be JsonMap.fromJson(Map m).
Untested, but off the top of my head, you should write your code like so:
abstract class JsonMap<T> {
Map<String, dynamic> toJson();
T fromJson(Map<String, dynamic> m);
}
The dot makes fromJson(Map m) a constructor of type T, or a static function belonging to type T. Without the dot, it is a function belonging to the abstract class JsonMap, returning type T. Specifying the map type is good practice if you know what it will be (like with json).

Resources