目次

一般的な型の問題の修正

型チェックに問題がある場合、このページが役立ちます。詳細については、Dart の型システムに関する情報と、その他の参考資料を参照してください。

トラブルシューティング

#

Dart は堅牢な型システムを強制します。つまり、変数の値が静的型と異なるコードを書くことはできません。型がintの変数は、小数点を含む数値を格納できません。Dart は、コンパイル時実行時に変数の値をその型と照合します。

変数に格納されている値が変数の静的型と異なる状況になることはありません。ほとんどの最新の静的に型付けされた言語と同様に、Dart は静的(コンパイル時)チェックと動的(実行時)チェックを組み合わせてこれを実現します。

たとえば、次の型エラーはコンパイル時に検出されます。

✗ 静的解析:失敗dart
List<int> numbers = [1, 2, 3];
List<String> string = numbers;

List<int>List<String>も互いのサブタイプではないため、Dart はこれを静的に除外します。

静的解析エラーの他の例と、その他のエラーの種類については、次のセクションを参照してください。

型エラーなし

#

予期したエラーまたは警告が表示されない場合は、最新バージョンの Dart を使用していて、IDE またはエディターが正しく構成されていることを確認してください。

dart analyzeコマンドを使用して、コマンドラインでプログラムの解析を実行することもできます。

解析が期待通りに機能していることを確認するには、次のコードを Dart ファイルに追加してみてください。

✗ 静的解析:失敗dart
bool b = [0][0];

正しく構成されていれば、アナライザーは次のエラーを生成します。

error - A value of type 'int' can't be assigned to a variable of type 'bool'. Try changing the type of the variable, or casting the right-hand type to 'bool'. - invalid_assignment

静的エラーと警告

#

このセクションでは、アナライザーまたは IDE から表示される可能性のあるいくつかのエラーと警告を修正する方法を示します。

静的解析ですべてのエラーを検出できるわけではありません。実行時のみ発生するエラーの修正については、ランタイムエラーを参照してください。

未定義のメンバー

#
error - The <member> '...' isn't defined for the type '...' - undefined_<member>

これらのエラーは、次の条件下で発生する可能性があります。

  • 変数は静的にスーパークラスであることがわかっていますが、コードはサブクラスを想定しています。
  • ジェネリッククラスに境界付きの型パラメーターがありますが、クラスのインスタンス作成式で型引数が省略されています。

例1:変数は静的にスーパークラスであることがわかっていますが、コードはサブクラスを想定しています

#

次のコードでは、アナライザーはcontext2Dが未定義であると文句を言います。

✗ 静的解析:失敗dart
var canvas = querySelector('canvas')!;
canvas.context2D.lineTo(x, y);
error - The getter 'context2D' isn't defined for the type 'Element'. Try importing the library that defines 'context2D', correcting the name to the name of an existing getter, or defining a getter or field named 'context2D'. - undefined_getter

修正:メンバーの定義を明示的な型宣言またはダウンキャストで置き換えます

#

querySelector()の戻り値の型はElement?!Elementに変換されます)ですが、コードはそれがCanvasElementcontext2Dを定義する)のサブタイプであると想定しています。canvasフィールドはvarとして宣言されているため、Dart はcanvasElementとして推論できます。

明示的なダウンキャストを使用してこのエラーを修正できます。

✔ 静的解析:成功dart
var canvas = querySelector('canvas') as CanvasElement;
canvas.context2D.lineTo(x, y);

そうでない場合は、単一の型を使用できない状況ではdynamicを使用します。

✔ 静的解析:成功dart
dynamic canvasOrImg = querySelector('canvas, img');
var width = canvasOrImg.width;

例2:省略された型パラメーターは型境界にデフォルトで設定されます

#

Iterableを拡張する境界付き型パラメーターを持つ次の**ジェネリッククラス**を考えてみましょう。

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

次のコードは、このクラスの新しいインスタンスを作成し(型引数を省略して)、そのcollectionメンバーにアクセスします。

✗ 静的解析:失敗dart
var c = C(Iterable.empty()).collection;
c.add(2);
error - The method 'add' isn't defined for the type 'Iterable'. Try correcting the name to the name of an existing method, or defining a method named 'add'. - undefined_method

List型にはadd()メソッドがありますが、Iterableにはありません。

修正:型引数を指定するか、下流のエラーを修正します

#

ジェネリッククラスが明示的な型引数なしでインスタンス化されると、各型パラメーターは、明示的に指定されている場合は型境界(この例ではIterable)、そうでない場合はdynamicにデフォルトで設定されます。

このようなエラーの修正には、ケースバイケースで対処する必要があります。元の設計意図をよく理解しておくことが役立ちます。

型引数を明示的に渡すと、型エラーを特定するのに効果的です。たとえば、コードを型引数としてListを指定するように変更すると、アナライザーはコンストラクタ引数の型ミスマッチを検出できます。適切な型のコンストラクタ引数(リストリテラルなど)を提供することで、エラーを修正します。

✔ 静的解析:成功dart
var c = C<List>([]).collection;
c.add(2);

無効なメソッドオーバーライド

#
error - '...'  isn't a valid override of '...' - invalid_override

これらのエラーは、通常、サブクラスが元のクラスのサブクラスを指定することでメソッドのパラメーター型を厳しくする場合に発生します。

#

次の例では、add()メソッドのパラメーターはint型(numのサブタイプ)であり、親クラスで使用されるパラメーター型です。

✗ 静的解析:失敗dart
abstract class NumberAdder {
  num add(num a, num b);
}

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

浮動小数点値がMyAdderに渡される次のシナリオを考えてみましょう。

✗ ランタイム:失敗dart
NumberAdder adder = MyAdder();
adder.add(1.2, 3.4);

オーバーライドが許可された場合、コードは実行時にエラーを発生させます。

修正:メソッドのパラメーター型を広げます

#

サブクラスのメソッドは、スーパークラスのメソッドが受け取るすべてのオブジェクトを受け入れる必要があります。

サブクラスの型を広げることで例を修正します。

✔ 静的解析:成功dart
abstract class NumberAdder {
  num add(num a, num b);
}

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

詳細については、メソッドをオーバーライドする際には適切な入力パラメーター型を使用するを参照してください。


型引数の不足

#
error - '...'  isn't a valid override of '...' - invalid_override

#

次の例では、SubclassSuperclass<T>を拡張しますが、型引数を指定していません。アナライザーはSubclass<dynamic>を推論し、その結果、method(int)で無効なオーバーライドエラーが発生します。

✗ 静的解析:失敗dart
class Superclass<T> {
  void method(T param) { ... }
}

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

修正:ジェネリックサブクラスの型引数を指定します

#

ジェネリックサブクラスが型引数を指定しないと、アナライザーはdynamic型を推論します。これにより、エラーが発生する可能性が高くなります。

サブクラスに型を指定することで、例を修正できます。

✔ 静的解析:成功dart
class Superclass<T> {
  void method(T param) { ... }
}

class Subclass extends Superclass<int> {
  @override
  void method(int param) { ... }
}

コードでジェネリック型引数を指定するようにする、厳格な生の型モードでアナライザーを使用することを検討してください。プロジェクトのanalysis_options.yamlファイルで厳格な生の型を有効にする例を次に示します。

yaml
analyzer:
  language:
    strict-raw-types: true

アナライザーの動作のカスタマイズの詳細については、静的解析のカスタマイズを参照してください。


予期しないコレクション要素型

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

これは、単純な動的なコレクションを作成し、アナライザーが予期しない方法で型を推論した場合に発生することがあります。後で異なる型の値を追加すると、アナライザーは問題を報告します。

#

次のコードは、いくつかの(Stringint)ペアでマップを初期化します。アナライザーは、そのマップが<String, int>型であると推論しますが、コードは<String, dynamic>または<String, num>のいずれかを想定しているようです。コードに(Stringdouble)ペアを追加すると、アナライザーは文句を言います。

✗ 静的解析:失敗dart
// Inferred as Map<String, int>
var map = {'a': 1, 'b': 2, 'c': 3};
map['d'] = 1.5;
error - A value of type 'double' can't be assigned to a variable of type 'int'. Try changing the type of the variable, or casting the right-hand type to 'int'. - invalid_assignment

修正:型を明示的に指定します

#

この例は、マップの型を<String, num>と明示的に定義することで修正できます。

✔ 静的解析:成功dart
var map = <String, num>{'a': 1, 'b': 2, 'c': 3};
map['d'] = 1.5;

あるいは、このマップが任意の値を受け付けるようにしたい場合は、型を<String, dynamic>と指定します。


コンストラクタ初期化リストの super() 呼び出し

#
error - The superconstructor call must be last in an initializer list: '...'. - super_invocation_not_last

このエラーは、コンストラクタの初期化リスト内でsuper()呼び出しが最後でない場合に発生します。

#
✗ 静的解析:失敗dart
HoneyBadger(Eats food, String name)
    : super(food),
      _name = name { ... }
error - The superconstructor call must be last in an initializer list: 'Animal'. - super_invocation_not_last

修正:super()呼び出しを最後に配置します。

#

コンパイラは、super()呼び出しが最後に現れることを前提としている場合、よりシンプルなコードを生成できます。

このエラーを修正するには、super()呼び出しを移動します。

✔ 静的解析:成功dart
HoneyBadger(Eats food, String name)
    : _name = name,
      super(food) { ... }

引数の型…は、パラメーターの型…に割り当てることができません

#
error - The argument type '...' can't be assigned to the parameter type '...'. - argument_type_not_assignable

Dart 1.xでは、dynamicはコンテキストに応じてトップタイプ(すべての型のスーパタイプ)とボトムタイプ(すべての型のサブタイプ)の両方でした。これは、たとえば、型Stringのパラメータを持つ関数を、dynamicのパラメータを持つ関数型を期待する場所に代入することが有効であったことを意味します。

しかし、Dart 2では、dynamic(またはObject?などの他のトップタイプ)以外の型パラメータを使用すると、コンパイル時エラーが発生します。

#
✗ 静的解析:失敗dart
void filterValues(bool Function(dynamic) filter) {}
filterValues((String x) => x.contains('Hello'));
error - The argument type 'bool Function(String)' can't be assigned to the parameter type 'bool Function(dynamic)'. - argument_type_not_assignable

修正:型パラメータを追加するか、dynamicから明示的にキャストします。

#

可能な場合は、型パラメータを追加することでこのエラーを回避します。

✔ 静的解析:成功dart
void filterValues<T>(bool Function(T) filter) {}
filterValues<String>((x) => x.contains('Hello'));

それ以外の場合は、キャストを使用します。

✔ 静的解析:成功dart
void filterValues(bool Function(dynamic) filter) {}
filterValues((x) => (x as String).contains('Hello'));

不正確な型推論

#

まれに、Dartの型推論が、ジェネリックコンストラクタ呼び出しにおける関数リテラル引数について間違った型を推論することがあります。これは主にIterable.foldに影響します。

#

次のコードでは、型推論によりaの型はNullと推論されます。

✗ 静的解析:失敗dart
var ints = [1, 2, 3];
var maximumOrNull = ints.fold(null, (a, b) => a == null || a < b ? b : a);

修正:適切な型を明示的な型引数として指定します。

#
✔ 静的解析:成功dart
var ints = [1, 2, 3];
var maximumOrNull =
    ints.fold<int?>(null, (a, b) => a == null || a < b ? b : a);

競合するスーパークラス

#

複数のスーパインターフェースをimplementsするクラスは、すべてのスーパインターフェースのすべてのメンバに対して有効なオーバーライドを実装できる必要があります。同じ名前の各メンバは、スーパインターフェース間で互換性のあるシグネチャが必要です。

スーパインターフェースには、競合するジェネリックを含めることはできません。クラスは、間接的なスーパインターフェースを含め、C<A>C<B>の両方を同時に実装することはできません。

#

次のコードでは、クラスCは競合するジェネリックインターフェースを持っています。一部のメンバに対する有効なオーバーライドの定義は不可能です。

✗ 静的解析:失敗dart
abstract class C implements List<int>, Iterable<num> {}

修正:一貫性のあるジェネリックを使用するか、推移的なインターフェースの重複を避けます。

#
✔ 静的解析:成功dart
abstract class C implements List<int> {}

ランタイムエラー

#

このセクションで説明されているエラーは、ランタイムで報告されます。

無効なキャスト

#

型安全性を確保するために、Dartは場合によってはランタイムチェックを挿入する必要があります。次のassumeStringsメソッドを考えてみましょう。

✔ 静的解析:成功dart
void assumeStrings(dynamic objects) {
  List<String> strings = objects; // Runtime downcast check
  String string = strings[0]; // Expect a String value
}

stringsへの代入は、dynamicList<String>に暗黙的にダウンスキャストしています(as List<String>と書いた場合と同じです)。そのため、ランタイムでobjectsに渡す値がList<String>の場合、キャストは成功します。

それ以外の場合は、ランタイムでキャストが失敗します。

✗ ランタイム:失敗dart
assumeStrings(<int>[1, 2, 3]);
Exception: type 'List<int>' is not a subtype of type 'List<String>'

修正:型を厳しくするか修正します。

#

特に空の集合の場合、型の不足は、意図した型付きコレクションではなく、<dynamic>コレクションが作成されることを意味することがあります。明示的な型引数を追加すると役立ちます。

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

ローカル変数をより正確に型指定し、推論を利用することもできます。

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

JSONや外部データソースなど、自分で作成しないコレクションを操作する場合は、ListなどのIterable実装によって提供されるcast()メソッドを使用できます。

こちらが推奨される解決策の例です。オブジェクトの型を厳しくします。

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

付録

#

共変キーワード

#

一部(めったに使用されない)コーディングパターンは、パラメータの型をサブタイプでオーバーライドすることで型を厳しくすることに依存していますが、これは無効です。この場合、covariantキーワードを使用して、意図的にこれを行っていることをアナライザーに伝えることができます。これにより、静的エラーは削除され、代わりにランタイムで無効な引数の型がチェックされます。

以下は、covariantの使用方法を示しています。

✔ 静的解析:成功dart
class Animal {
  void chase(Animal x) { ... }
}

class Mouse extends Animal { ... }

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

この例ではサブタイプでcovariantを使用していますが、covariantキーワードはスーパークラスまたはサブクラスのメソッドのどちらにも配置できます。通常、スーパークラスのメソッドに配置するのが最適です。covariantキーワードは単一のパラメータに適用され、セッターとフィールドでもサポートされています。