Restrictions on Type in dart - 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

Related

class parameter or class identifier query

I am new in Dart and works on an app that has a class look like this:
abstract class BaseUseCase <In,Out> {}
My question is then, what is In and Out?
In and Out are type arguments. They are used to allow the code in the class to use objects of an unknown type while remaining consistent and type-safe.
For example, say you wanted to have a method in a class that would take a list of any type, perform a string conversion operation on every element, and then return a strongly typed map of results:
class Stringifier<T> {
Map<T, String> stringify(List<T> input) =>
{for (final entry in input) entry: input.toString()};
}
void main() {
Stringifier<int>().stringify([1, 2, 3]);
// Result: <int, String>{1: '1', 2: '2', 3: '3'}
}
Note that the return type and input argument type use the generic T type. This ensures that only a list of the given type can be passed in, and that the resultant map will have the correct key type.
Type arguments can be used in other declarations as well, such as function declarations - indeed, the example above can be simplified, and declared outside a class:
Map<T, String> stringify(List<T> input) { /* ... */ }
More information on generics can be found in the Dart Language Tour, as well as in the "Generics" section of the Dart 2 language specification.

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

How to get the subtypes of a generic type using `DartType` from `analyzer` package?

How can I get the subtypes of an element using the class DartType from the analyzer package?
for example if the type is List<String>, I would like to get String. Also will be useful to get if the type is generic.
Another more complex example would be Map<String, String> where I want to get a list of the subtypes, in this case: [String, String].
This one is a little tricky - because DartType actually itself has some super types - the one that will interest you here is ParameterizedType:
import 'package:analyzer/dart/element/type.dart';
Iterable<DartType> getGenericTypes(DartType type) {
return type is ParameterizedType ? type.typeArguments : const [];
}
I don't know if it's possible to know if the type is generic - after all, it's just a type. But you can check if the type accepts generic parameters, again, using ClassElement:
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
bool canHaveGenerics(DartType type) {
final element = type.element;
if (element is ClassElement) {
return element.typeParameters.isNotEmpty;
}
return false;
}
Hope that helps!

Generics type parameter wildcard

I would like to create an abstract class which takes a type parameter and the constructor of that class should be passed another Action eg.
abstract class Action<Tc> {
public function __construct(private ?Action<*> $onSuccess = null) {}
}
How can I express a type parameter wildcard ie. "?" (Java) or "_" (Scala) in Hack?
Hack doesn't have wildcard type parameters right now, so the closest you can get is actually specifying a dummy type parameter that you don't actually need, e.g.,
abstract class Action<Tc, Ta> {
public function __construct(private ?Action<Ta> $onSuccess = null) {}
// ...
}
Depending on how exactly you use the $onSuccess member variable, you may want it to be some specific subclass of Action<T> to be determined later, and so you may want something like this:
abstract class Action<Tc, Ta, To as Action<Ta>> {
public function __construct(private ?To $onSuccess = null) {}
// ...
}
However, I question whether the "dummy" types above above are really a dummy -- the vast, vast majority of use cases of Action<T> are going to care what exactly the T is, otherwise how exactly would you use the Action<T>? (There are certainly rare cases where you don't care about the T at a callsite, but they are, well, rare and so I encourage you to consider whether that is actually your case as you build out this functionality.)
Not sure about a wildcard, but could this achieve what you want?
<?hh
abstract class Action<T1 as Action, T2> {
public function __construct(private ?T1 $onSuccess = null, private ?T2 $bla = null) {}
}
class ActionA<T1 as Action, T2> extends Action<T1, T2> {}
class ActionB<T1 as Action, T2> extends Action<T1, T2> {}
class ActionC<T1 as Action, T2> extends Action<T1, T2> {}
$action = new ActionA(new ActionB(new ActionC(null)));
var_dump($action);
When I run this against HHVM 3.1.0, I get:
object(ActionA)#1 (2) {
["onSuccess":"Action":private]=>
object(ActionB)#2 (2) {
["onSuccess":"Action":private]=>
object(ActionC)#3 (2) {
["onSuccess":"Action":private]=>
NULL
["bla":"Action":private]=>
NULL
}
["bla":"Action":private]=>
NULL
}
["bla":"Action":private]=>
NULL
}
And the 3.1.0 type checker also returns "No errors!".
However, the T1 as Action statement on the abstract class doesn't appear to be enforcing. For instance, I can change the instantiation line to:
$action = new ActionA(new ActionB(new ActionC(new DateTime())));
And it hums along fine, with the typechecker returning no errors still. And this is after taking the class definitions out into their own file with <?hh // strict.
So not really your answer, but perhaps close? The behavior above might suggest Hack has some issues with this sort of pattern?

Resources