Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

type {superclass} is not a subtype of type {class extending it} #37742

Closed
CorvetteCole opened this issue Aug 5, 2019 · 4 comments
Closed

type {superclass} is not a subtype of type {class extending it} #37742

CorvetteCole opened this issue Aug 5, 2019 · 4 comments
Labels
type-question A question about expected behavior or functionality

Comments

@CorvetteCole
Copy link

  • Dart SDK Version: 2.4.0
  • Build machine: Windows
  • Platform: Flutter, tested on Android

I initially thought I was being stupid, but after thoroughly reviewing the documentation with a coworker I am not entirely convinced it isn't a bug.

I am using the Dart http library and attempting to extend the "Response" object. Basically, my class is extending the "Response" object from the library as well as containing an additional ConnectionError object which is self-explanatory.

I am implementing all the constructor parameters of the http Response class. Therefore, since I am extending it, I should be able to do this:
ResponseWithError response = await http.post(url, headers: headers, body: json.encode(alarmSearchRequest.toJson())); right?

Well the problem is, I get this error: Unhandled Exception: type 'Response' is not a subtype of type 'ResponseWithError'

I do not understand what could possibly be wrong with this code that makes it not want to cast. I have pasted the ResponseWithError class below (with the custom error code removed, bug is still present with this exact code).

The kicker is, if I copy and paste the http Response class and extend BaseRequest it works fine with casting. Please help me understand this behavior, thanks

import 'package:http/http.dart' as http;

class ResponseWithError extends http.Response {

  ResponseWithError(String body, int statusCode,
      {http.BaseRequest request,
      Map<String, String> headers = const {},
      bool isRedirect = false,
      bool persistentConnection = true,
      String reasonPhrase})
      : super(body, statusCode,
            request: request,
            headers: headers,
            isRedirect: isRedirect,
            persistentConnection: persistentConnection,
            reasonPhrase: reasonPhrase);

  ResponseWithError.bytes(List<int> bodyBytes, int statusCode,
      {http.BaseRequest request,
      Map<String, String> headers = const {},
      bool isRedirect = false,
      bool persistentConnection = true,
      String reasonPhrase})
      : super.bytes(bodyBytes, statusCode,
            request: request,
            headers: headers,
            isRedirect: isRedirect,
            persistentConnection: persistentConnection,
            reasonPhrase: reasonPhrase);
}
@renatoathaydes
Copy link

Dart does not auto-convert types like that. http.post returns a Response, and Response cannot be assigned to your type, ResponseWithError, because it does not implement your type.

You claimed in the Dart Gitter chat that this would work in Java, but that's not the case either. Type rules in Java and Dart are quite similar.

What would've worked in either language is something like this:

class ResponseWithError {
    final http.Response response;
    // more fields here?
    ResponseWithError(this.response);
}

And then, where you make the http call:

ResponseWithError response = ResponseWithError(await http.post( .... ));

@CorvetteCole
Copy link
Author

I must've been mistake then. I that is a way I considered doing it, but typing out ResponseWithError.response.statusCode felt really clunky which is why I was trying to avoid that

@renatoathaydes
Copy link

If you really must, you can implement the delegate pattern so that ResponseWithError implements http.Response by delegating all calls to the internal response object.
With @delegate, this would be a one-liner. See dart-lang/language#3748 which is asking for this to be introduced to the language itself (as in Kotlin, for example).

This can also be implemented with codegen, as was done in zengen, but that hasn't been upgraded to Dart 2.0 yet (it's a lot of work! I am trying to help). I couldn't find a package that currently supports that, unfortunately... Maybe you can write one :)

@natebosch
Copy link
Member

Dart's type system is nominal and you can't cast a instance of a supertype to a subtype. Sometimes you might statically have a supertype which is at runtime a subtype, and then the cast would work. As @renatoathaydes points out Java works very similarly here.

class A {}

class B implements A {} // could be implements or extends

void main() {
  var isA = A();
  print(isA is B); // False, an A is not a B
  var isB = B();
  doStuff(isB);
}

var doStuff(A a) {
  print(a is B); // May be true, if at runtime the arg was actually a B
}

@natebosch natebosch added the type-question A question about expected behavior or functionality label Aug 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-question A question about expected behavior or functionality
Projects
None yet
Development

No branches or pull requests

3 participants