Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to require generic type arguments on a Dart class?

Tags:

flutter

dart

A common question, specifically since Dart 2, is if it is possible to require some or all generic type arguments on some or all types - for example List<int> instead of List or MyType<Foo> instead of MyType.

It's not always clear what the intention is though - i.e. is this a matter of style (you/your team likes to see the types), to prevent bugs (omitting type arguments seems to cause more bugs for you/your team), or as a matter of contract (your library expects a type argument).

For example, on dart-misc, a user writes:

Basically, if I have this:

abstract class Mixin<T> {}

I don't have to specify the type:

// Works class Cls extends Object with Mixin<int> {} // ...also works
class Cls extends Object with Mixin {}

Is there some way to make the second one not allowed?

like image 558
matanlurey Avatar asked Sep 12 '18 01:09

matanlurey


People also ask

How do you use Generics in Dart?

Dart supports the use of generic types (often denoted as T ) in three general places: In a function's arguments. In local variables inside a function. In a function's return type.

Which types can be used as arguments of a generic type?

The actual type arguments of a generic type are. reference types, wildcards, or. parameterized types (i.e. instantiations of other generic types).

What is generic class in flutter?

Introduction: Generics are utilized to apply more grounded type checks at the compile time. They implement type-safety in code. For instance, in collections, the type-safety is authorized by holding a similar kind of information. Generics help composes reusable classes, methods/functions for various information types.


1 Answers

Strictly speaking, yes, and no.

If you want to enforce that type arguments are always used in your own projects (instead of relying on type inference or defaults), you can use optional linter rules such as always_specify_types. Do note this rule violates the official Dart style guide's recommendation of AVOID redundant type arguments on generic invocations in many cases.

If you want to enforce that generic type arguments are always used when the default would be confusing - such as List implicitly meaning List<dynamic>, no such lint exists yet - though we plan on adding this as a mode of the analyzer: https://github.com/dart-lang/sdk/issues/33119.


Both of the above recommendations will help yourself, but if you are creating a library for others to use, you might be asking if you can require a type argument to use your class. For example, from above:

abstract class Mixin<T> {}
abstract class Class extends Object with Mixin {}

The first thing you could do is add a default bounds to T:

// If T is omitted/not inferred, it defaults to num, not dynamic.
abstract class Mixin<T extends num> {}

If you want to allow anything but want to make it difficult to use your class/mixin when T is dynamic you could choose a different default bound, for example Object, or even better I recommend void:

In practice, I use void to mean “anything and I don’t care about the elements”

abstract class Mixin<T extends void> {
  T value;
}

class Class extends Mixin {}

void main() {
  var c = Class();
  // Compile-time error: 'oops' isn't defined for the class 'void'.
  c.value.oops();
}

(You could also use Object for this purpose)

If this is a class under your control, you could add an assertion that prevents the class from being used in a way you don't support or expect. For example:

class AlwaysSpecifyType<T> {
  AlwaysSpecifyType() {
    assert(T != dynamic);
  }
}

Finally, you could write a custom lint or tool to disallow certain generic type arguments from being omitted, but that is likely the most amount of work, and if any of the previous approaches work for you, I'd strongly recommend those!

like image 115
matanlurey Avatar answered Oct 08 '22 23:10

matanlurey