Let's look at the type error more closely:
type '() => Future<HttpClientRequest>' is not a subtype of type '(() => FutureOr<_HttpClientRequest>)?' of 'onTimeout'.
You supply a callback function that returns a Future<HttpClientRequest>.
What's expected is a function that returns a FutureOr<_HttpClientRequest>.
FutureOr<T> is a special union of types T and Future<T>, so a function that returns a Future<HttpCientRequest> is a subtype of (i.e., is compatible with) a function that returns a FutureOr<HttpClientRequest>. However, what's expected is a FutureOr<_HttpClientRequest>. _HttpClientRequest? Where did that come from, and why isn't that compatible?
Even without knowing the implementation details, we can presume that _HttpClientRequest is some subtype of HttpClientRequest. The error message implies that when we call HttpClient.get, the static (known at compile-time) type of the returned object is Future<HttpClientRequest>, but the actual runtime type is Future<_HttpClientRequest>. If type Derived is a subtype of Base, then Dart also treats GenericClass<Derived> as a subtype of GenericClass<Base>. Normally this is okay, and returning a narrower subtype of what a function is declared to return is safe.
The problem occurs when you then try to call .timeout on that returned Future<_HttpClientRequest>. Future<T>.timeout's callback must return a FutureOr<T>. However, you're calling .timeout on an object whose runtime type is Future<_HttpClientRequest>, so your callback must return an _HttpClientRequest too. Returning the base class type (HttpClientRequest) is not valid since you can't return a broader type where a narrower type is expected.
(See Dart gives Unhandled Exception: type is not a subtype of type of 'value' for another case where treating GenericClass<Derived> as a subtype of GenericClass<Base> can lead to surprising runtime errors.)
TL;DR
As far as the analyzer and compiler know, you're calling .timeout on a Future<HttpClientRequest> with a callback that returns an HttpClientRequest, so there's no compile-time error. However, at runtime, you're actually calling .timeout on a Future<_HttpClientRequest> with a callback that returns an HttpClientRequest, so you end up with a runtime error.
How can you fix this?
Some options:
- Consider filing a Dart SDK issue about
HttpClient.get returning an object whose runtime type is Future<_HttpClientRequest> instead of a Future<HttpClientRequest>. - Since
_HttpClientRequest is a private type, you can't actually return an object of that type yourself. You can, however, use Future.value to construct a new Future<HttpClientRequest> out of the Future<_HttpClientRequest>: // `client.get` might return a `Future` that completes to a subtype of // `HttpClientRequest`. var request = Future<HttpClientRequest>.value(client.get('10.255.255.1', 80, '')); request.timeout(const Duration(seconds: 1), onTimeout: () { print('timed out'); return request; }); return request; }
- Avoid the weirdness with the callback type by not using
Future.timeout with a callback and instead catching the resulting TimeoutException.
I'll also point out that Future.timeout returns a new Future, but you're returning the original Future, and the value returned by the timeout callback won't ever be used. I don't know if that's what you intend, although in this case it's probably fine if all you want to do is log that a request took too long.
return request;I getError: A non-null value must be returned since the return type 'FutureOr<HttpClientRequest>' doesn't allow null.return 'my string';I getError: A value of type 'String' can't be returned from a function with return type 'FutureOr<HttpClientRequest>'. My understanding is that the documentation example returns'timeout'because the example type is aFuture<String>.