How can I pass-in non-constant method in Dart Annotations?
Here is my annotation:
class Injectable {
final String name;
final Scope scope;
final List<Provider>? provider;
const Injectable({this.name = '', this.scope = Scope.factory, this.provider});
}
class Provider<T> {
final Type? useClass;
final Object? useValue;
final T? Function()? usefactory;
final List<Type> deps;
const Provider(
{this.useClass, this.useValue, this.usefactory, this.deps = const []});
}
This works fine:
But when I try to directly pass-in the function. I am getting a compile error:
Any idea what is happening?
Error: Arguments of a constant creation must be constant expressions. (Documentation) Try making the argument a valid constant, or use 'new' to call the constructor.
Invalid constant value.
The argument type 'ServiceA' can't be assigned to the parameter type 'ServiceA? Function()?'. (Documentation)
Please try to remove the brackets from inject<ServiceA>(). Just make it inject<ServiceA>.
usefactory-s type is a function, but in your case, you are passing ServiceA type data.
Related
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.
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));
My code is pretty simple:
typedef GenericCallback = void Function<T>(T);
void main() {
GenericCallback callback = <String>(String message) => printMessage(message);
}
void printMessage([String errorMessageReason]) {
print(errorMessageReason ?? '');
}
And DartPad gives me this error at the word message in printMessage(message):
Error: The argument type 'String/*1*/' can't be assigned to the parameter type 'String/*2*/'.
- 'String/*1*/' is from 'unknown'.
- 'String/*2*/' is from 'dart:core'.
It looks like Dart is getting the reference from one String and not the other, how's this even possible?
Since you've done typedef GenericCallback = void Function<T>(T) you need to provide a generic method which matches the signature as a callback. The tricky part here is, you are doing that but not in the way you think.
In this line it looks like you're trying to specify the type for the closure you've created:
GenericCallback callback = <String>(String message) => printMessage(message);
However, Dart's rules for naming generic type parameters are strange in that you can use the names of existing types as the name of the type parameter. In other words, the following lines are all functionally identical and will provide a similar error:
GenericCallback callback = <String>(String message) => printMessage(message);
GenericCallback callback = <T>(T message) => printMessage(message);
GenericCallback callback = <int>(int message) => printMessage(message);
These generic closures are all completely valid and even built-in types like int and String will be treated as the names for type parameters in the scope of the closure.
In order to fix your error, you'll want to change the type parameter String to have a different name that doesn't collide with a core type, and do one of the following:
Update your invocation of printMessage to cast message to a String, although this will fail if T isn't of type String when the closure is called.
GenericCallback callback = <T>(T message) => printMessage(message as String);
Update your typedef to expect a String parameter
typedef GenericCallback = void Function<T extends String>(T);
GenericCallback callback = <T extends String>(T message) => printMessage(message);
This is an easy mistake to make if you've come from a language which allows for template/generic specialization like C++. Keep in mind that, at least currently, you can't specialize a generic method or object and the generic type isn't assigned until the method is actually called or object is created.
In C# it is possible to strongly type function variables and parameters.
What is the dart equivalent of this c# code?
Func<bool, String> myFunc;
I am aware of the Function type but it doesn't allow me to define my input and output types, is there any way to acheive this in dart?
Function allows you to specify your input and output types, and can be used as method parameters and return types, in generics, or they can be given a name using a typedef.
/// As a method parameter.
void takesACallback(void Function(int, String) callback) {}
/// As a type parameter.
final mapOfFunctions = <int, String Function(bool)>{};
/// Named with a typedef.
typedef MyCallback = void Function(int);
/// Using the typedef
void myMethod(MyCallback myCallback) {}
I have this annotation:
class Handler {
final Function onListen;
final Function onPause;
final Function onResume;
final Function onCancel;
const Handler({this.onListen, this.onPause, this.onResume, this.onCancel});
}
and use it like this:
abstract class Test implements ViewModel<TestController> {
static onListen() {
print('onListen');
}
#Handler(onListen: onListen)
Stream<String> get messages;
factory Test() = _$Test;
Test._();
}
This works so far but is there a way to use a non static method as parameter for my annotation?
When I remove the static I get this error: Arguments of a constant creation must be constant expressions.
No, instance methods can't be const and annotations have to be const,
Therefore you can only have static methods or top-level functions as parameters for annotations.