Home > Enterprise >  How to make T extends two types?
How to make T extends two types?

Time:05-18

void foo<T extends num, String> (T t) {
  if (t is String) {
    String s = t; // Error
  }
}

A value of type 'T' can't be assigned to a variable of type 'String'.

CodePudding user response:

You won't be able to do this with base Dart as your generic type T can only extends one class. The only way I would see such a behavior feasible would be by using a 3rd party packages such as dartz with its Either type.

Example

void foo<T extends num>(Either<T, String> t) {
  final String s;
  if (t.isRight()) {
    s = (t as Right<T, String>).value;
  } else {
    s = (t as Left<T, String>).value.toStringAsFixed(3);
  }
  print(s);
}

foo(Left(1.0)); // prints '1.000'
foo<int>(Right('bar')); // prints 'bar'

CodePudding user response:

There is no syntax to specify that a generic type implement multiple interfaces, so there is no way for this to work with compile-time checks.

Furthermore, your particular example can't work because num and String cannot be extended nor implemented, so it's impossible to have a type that implements both.

If we change your example, which relies on a runtime check, to use two custom types, it still won't work:

class C1 {}

class C2 {
  void f() => print('C2.f');
}

class C3 implements C1, C2 {
  @override
  void f() => print('C3.f');
}

void foo<T extends C1>(T t) {
  if (t is C2) {
    t.f(); // 'f' isn't defined for the type <unknown>
  }
}

See https://github.com/dart-lang/language/issues/2047: t isn't related to C2, so the is C2 check unfortunately will not automatically promote it to C2. You instead can use a runtime cast:

void foo<T extends C1>(T t) {
  if (t is C2) {
    (t as C2).f();
  }
}

or upcast to Object/dynamic first:

void foo<T extends C1>(T t) {
  Object t0 = t;
  if (t0 is C2) {
    t0.f();
  }
}

But really you should just use T extends C3 if possible.

  •  Tags:  
  • dart
  • Related