sdk
sdk copied to clipboard
Static analysis for a `@returnType` annotation for Function types
This request is specifically for the Angular team, but can be used generally. The problem is as follows: given a function with the following signature:
void foo(Function cb)
there is no way to specify that cb must return a non-nullable type. For whatever reason, the type of cb cannot be function-typed (like int Function(bool)), and the most narrow type that can be specified is Function. The return value can be null-checked at runtime, but it would be much better to catch this statically. One idea is an annotation like so:
void foo(@returnType<Object> Function cb)orvoid foo(@ReturnType(Object) Function cb)
With recent language versions of Dart, either of these could specify a function type or a type with type arguments:
void foo(@returnType<int Function(bool)> Function cb)void foo(@returnType(int Function(bool)) Function cb)void foo(@ReturnType<List<String>> Function cb)void foo(@ReturnType(List<String>) Function cb)
I think I prefer the @returnType<Object> API.
The rules for this annotation are:
- It can only annotate a parameter (or field?)
- The type of the variable (parameter, field, ...) that it annotates must be exactly
Function - Given an annotation like
@returnType<X>, any argument given for this parameter must be function-typed and the return type on the function-type must be assignable toX.- Alternatively, we could also allow an argument which is a direct reference to a variable (parameter, ...) which is itself annotated with
@returnType. I think the decision about allowing this is just how difficult it is to implement.
- Alternatively, we could also allow an argument which is a direct reference to a variable (parameter, ...) which is itself annotated with
Open questions:
- The format of the annotation; two options listed above
- Are fields important? Easier to only allow it on parameters to start?
- What about inheritance? If a method
D.foverrides a methodC.f, with a parameter annotated with@returnType, must the corresponding parameter inD.falso be explicitly annotated? - For the assignability bit, would
dynamicbe allowed, as the only type which can be explicitly cast to other types? Explicitly bandynamicexcept for@returnType<dynamic>?
CC @leonsenft @jakemac53 who helped design this yesterday.
- What about inheritance? If a method
D.foverrides a methodC.f, with a parameter annotated with@returnType, must the corresponding parameter inD.falso be explicitly annotated?
Given normal override rules I would say no, it's allowed to expand the type to a more general type (ie: Function) in this case. If it wanted to call super though then it would have to duplicate the annotation.
Would or could such an annotation affect type flow analysis?
For example, would you need to still null check the return value from an annotated function?
int square(@returnType<int Function()> Function cb) {
final value = cb()!; // Would the ! still be necessary, or could it be omitted?
return cb * cb;
}
Or would this simply produce an error at the call site of square()?
Would or could such an annotation affect type flow analysis?
Nope.
For example, would you need to still null check the return value from an annotated function?
Yes, if it does today. (E.g. if the default return type is dynamic, which I think it is. Then value would be dynamic without the ! and cb * cb would be dynamic invocation.
Does this still make sense? Perhaps if there was no other user, and it wasn't needed so far, let's close the issue?
I think this request still makes sense. Our team keeps issues open even if we have no plans to implement it, for searching, tracking, and discussion purposes. But we can make it P4 if Angular has chugged along OK without it, and no one else has chimed in that it would be useful.