Contents

Fixing common type problems

如果您在类型检查方面遇到问题,此页面可以为您提供帮助. 要了解更多信息,请阅读Dart的类型系统 ,并查看其他资源 .

Troubleshooting

Am I really using type-safe Dart?

如果没有看到预期的错误或警告,请确保使用的是最新版本的Dart.

或者,尝试将以下代码添加到文件中:

bool b = [0][0];

使用类型安全的Dart,分析仪会产生以下错误:

error • A value of type 'int' can't be assigned to a variable of type 'bool' • invalid_assignment

Static errors and warnings

本节显示了如何解决可能从分析仪或IDE中看到的一些错误和警告.

静态分析无法捕获所有错误. 有关修复仅在运行时出现的错误的帮助,请参阅" 运行时错误" .

Undefined member

error • The <member> '...' isn't defined for the class '...' • undefined_<member>

在以下情况下会出现这些错误:

  • 静态已知变量是某些超类型,但是代码假定为子类型.
  • 泛型类具有有界的类型参数,但是该类的实例创建表达式省略了type参数.

Example 1: A variable is statically known to be some supertype, but the code assumes a subtype

在以下代码中,分析器抱怨context2D未定义:

var canvas = querySelector('canvas');
canvas.context2D.lineTo(x, y);
error • The getter 'context2D' isn't defined for the class 'Element' • undefined_getter

Fix: Replace the definition of the member with an explicit type declaration or a downcast

querySelector()方法静态返回一个Element,但是代码假定它返回定义了context2D的子类型CanvasElement. canvas字段声明为var ,在没有强模式的Dart 1.x版本中,将其键入为dynamic并使所有错误静音. Dart推断canvas为元素.

您可以使用显式类型声明来解决此错误:

CanvasElement canvas = querySelector('canvas');
canvas.context2D.lineTo(x, y);

上面的代码在允许隐式强制转换时通过了静态分析.

您还可以使用显式下调:

var canvas = querySelector('canvas') as CanvasElement;
canvas.context2D.lineTo(x, y);

否则,在不能使用单一类型的情况下使用dynamic

dynamic canvasOrImg = querySelector('canvas, img');
var width = canvasOrImg.width;

Example 2: Omitted type parameters default to their type bounds

考虑以下带有扩展Iterable有界类型参数的 泛型类

class C<T extends Iterable> {
  final T collection;
  C(this.collection);
}

以下代码创建此类的新实例(省略type参数)并访问其collection成员:

var c = C(Iterable.empty()).collection;
c.add(2);
error • The method 'add' isn't defined for the class 'Iterable'. • lib/bounded/instantiate_to_bound.dart:7:5 • undefined_method

虽然List类型具有add()方法,但Iterable没有.

Fix: Specify type arguments or fix downstream errors

在Dart 1.x中,当在没有显式类型参数的情况下实例化泛型类时,将假定为dynamic的. 这就是为什么在上面的代码摘录中, cdynamic类型的,并且c.add()没有错误报告.

在Dart 2中,如果在没有显式类型实参的情况下实例化泛型类,则如果显式给出了每个类型形参,则每个类型形参默认为其类型绑定(在此示例中为Iterable ),否则为dynamic .

您需要逐案解决此类错误. 这有助于对原始设计意图有很好的了解.

显式传递类型参数是帮助识别类型错误的有效方法. 例如,如果更改代码以将List指定为类型参数,则分析器可以检测到构造函数参数中的类型不匹配. 通过提供适当类型的构造函数参数来修复错误:

var c = C<List>([]).collection;
c.add(2);

Invalid method override

error • '...'  isn't a valid override of '...'  • invalid_override

当子类通过指定原始类的子类来加强方法的参数类型时,通常会发生这些错误.

Example

在以下示例中, add()方法的参数类型为int ,它是num的子类型,它是父类中使用的参数类型.

abstract class NumberAdder {
  num add(num a, num b);
}

class MyAdder extends NumberAdder {
  int add(int a, int b) => a + b;
}
error • 'MyAdder.add' ('int Function(int, int)') isn't a valid override of 'NumberAdder.add' ('num Function(num, num)') • invalid_override

请考虑以下情况,其中将浮点值传递给MyAdder:

NumberAdder adder = MyAdder();
adder.add(1.2, 3.4);

如果允许重写,则代码将在运行时引发错误.

Fix: Widen the method’s parameter types

子类的方法应接受超类的方法采用的每个对象.

通过扩展子类中的类型来修复示例:

abstract class NumberAdder {
  num add(num a, num b);
}

class MyAdder extends NumberAdder {
  num add(num a, num b) => a + b;
}

有关更多信息,请参见覆盖方法时使用正确的输入参数类型 .


Missing type arguments

error • '...'  isn't a valid override of '...'  • invalid_override

Example

在以下示例中, Subclass扩展了Superclass<T> ,但未指定类型参数. 分析器推断Subclass<dynamic> ,这将导致对method(int)的无效覆盖错误.

class Superclass<T> {
  void method(T t) { ... }
}

class Subclass extends Superclass {
  void method(int i) { ... }
}
error • 'Subclass.method' ('void Function(int)') isn't a valid override of 'Superclass.method' ('void Function(dynamic)') • invalid_override

Fix: Specify type arguments for the generic subclass

当泛型子类忽略指定类型参数时,分析器将推断dynamic类型. 这可能会导致错误.

您可以通过在子类上指定类型来修复示例:

class Superclass<T> {
  void method(T t) { ... }
}

class Subclass extends Superclass<int> {
  void method(int i) { ... }
}

Unexpected collection element type

error • A value of type '...' can't be assigned to a variable of type '...' • invalid_assignment

当您创建一个简单的动态集合并且分析器以您意想不到的方式推断类型时,有时会发生这种情况. 以后添加其他类型的值时,分析器将报告问题.

Example

以下代码使用几对(String,int)对初始化一个映射. 分析器推断该映射为<String, int>类型<String, int>但代码似乎假定<String, dynamic><String, num> . 当代码添加(字符串,浮点数)对时,分析器将抱怨:

// Inferred as Map<String, int>
var map = {'a': 1, 'b': 2, 'c': 3};
map['d'] = 1.5; // a double is not an int
error • A value of type 'double' can't be assigned to a variable of type 'int' • invalid_assignment

Fix: Specify the type explicitly

可以通过将映射的类型显式定义为<String, num>来修复示例.

var map = <String, num>{'a': 1, 'b': 2, 'c': 3};
map['d'] = 1.5;

或者,如果您希望此映射接受任何值,则将类型指定为<String, dynamic> .


Constructor initialization list super() call

error • The super call must be last in an initializer list (see https://goo.gl/EY6hDP): '...' • invalid_super_invocation

super()调用不在构造函数的初始化列表中最后时,会发生此错误.

Example

HoneyBadger(Eats food, String name)
    : super(food),
      _name = name { ... }
error • The super call must be last in an initializer list (see https://goo.gl/EY6hDP): 'super(food)' • invalid_super_invocation

Fix: Put the super() call last

如果编译器依赖最后出现的super()调用,则可以生成更简单的代码.

通过移动super()调用来解决此错误:

HoneyBadger(Eats food, String name)
    : _name = name,
      super(food) { ... }

The function expression type … isn’t of type …

error • The function expression type '...' isn't of type '...'. This means its parameter or return type doesn't match what is expected. Consider changing parameter type(s) or the returned type(s) • invalid_cast_function_expr

在Dart 1.x中,根据上下文的不同, dynamic既是顶部类型 (所有类型的超类型)又是底部类型 (所有类型的子类型). 这意味着将具有String类型参数的函数分配给期望具有dynamic参数的函数类型的位置是有效的.

但是,在Dart 2中传递dynamic以外的其他内容(或另一种顶部类型(如Object )或特定的底部类型(如Null ))会导致编译时错误.

Example

typedef Filter = bool Function(dynamic any);
Filter filter = (String x) => x.contains('Hello');
error • The function expression type 'bool Function(String)' isn't of type 'bool Function(dynamic)'. This means its parameter or return type doesn't match what is expected. Consider changing parameter type(s) or the returned type(s) • invalid_cast_function_expr

Fix: Add type parameters or cast from dynamic explicitly

如果可能,请通过添加类型参数来避免此错误:

typedef Filter<T> = bool Function(T any);
Filter<String> filter = (String x) => x.contains('Hello');

否则使用强制转换:

typedef Filter = bool Function(dynamic any);
Filter filter = (x) => (x as String).contains('Hello');

Runtime errors

与Dart 1.x不同,Dart 2实施声音类型系统. 粗略地讲,这意味着您无法陷入变量中存储的值与变量的静态类型不同的情况. 像大多数现代的静态类型语言一样,Dart通过静态(编译时)和动态(运行时)检查的组合来完成此任务.

例如,在编译时(禁用隐式强制转换选项时)检测到以下类型错误:

List<int> numbers = [1, 2, 3];
List<String> string = numbers;

由于List<int>List<String>都不是另一个子类型,因此Dart会静态将其排除. 您可以在Unexpected collection element type中看到这些静态分析错误的其他示例.

本节其余部分讨论的错误将在运行时报告.

Invalid casts

To ensure type safety, Dart needs to insert runtime checks in some cases. Consider:

assumeStrings(List<Object> objects) {
  List<String> strings = objects; // Runtime downcast check
  String string = strings[0]; // Expect a String value
}

The assignment to strings is downcasting the List<Object> to List<String> implicitly (as if you wrote as List<String>), so if the value you pass in objects at runtime is a List<String>, then the cast succeeds.

否则,强制转换将在运行时失败:

assumeStrings(<int>[1, 2, 3]);
Exception: type 'List<int>' is not a subtype of type 'List<String>'

Fix: Tighten or correct types

有时,缺少类型(尤其是带有空集合的类型)意味着将创建一个<dynamic>集合,而不是您想要的类型化集合. 添加显式类型参数可以帮助:

var list = <String>[];
list.add('a string');
list.add('another');
assumeStrings(list);

您还可以更精确地键入局部变量,并让推断帮助:

List<String> list = [];
list.add('a string');
list.add('another');
assumeStrings(list);

如果您正在使用未创建的集合(例如从JSON或外部数据源),则可以使用List和其他集合类提供的cast()方法.

这是首选解决方案的示例:收紧对象的类型.

Map<String, dynamic> json = fetchFromExternalSource();
var names = json['names'] as List;
assumeStrings(names.cast<String>());

如果您不能收紧字体或使用cast ,则可以用其他方式复制对象. 例如:

Map<String, dynamic> json = fetchFromExternalSource();
var names = json['names'] as List;
// Use `as` and `toList` until 2.0.0-dev.22.0, when `cast` is available:
assumeStrings(names.map((name) => name as String).toList());

Appendix

The covariant keyword

一些(很少使用的)编码模式依靠通过用子类型覆盖参数的类型来加紧类型,这是无效的. 在这种情况下,您可以使用covariant关键字来告诉分析器您有意这样做. 这样可以消除静态错误,并在运行时检查无效的参数类型.

下面显示了如何使用covariant

class Animal {
  void chase(Animal x) { ... }
}

class Mouse extends Animal { ... }

class Cat extends Animal {
  void chase(covariant Mouse x) { ... }
}

尽管此示例显示了在子类型中使用covariant ,但可以将covariant关键字放置在超类或子类方法中. 通常,超类方法是放置它的最佳位置. covariant关键字适用于单个参数,并且在设置方法和字段中也受支持.

by  ICOPY.SITE