一般的な型の問題の修正
型チェックに問題がある場合、このページが役立ちます。詳細については、Dart の型システムに関する情報と、その他の参考資料を参照してください。
トラブルシューティング
#Dart は堅牢な型システムを強制します。つまり、変数の値が静的型と異なるコードを書くことはできません。型がint
の変数は、小数点を含む数値を格納できません。Dart は、コンパイル時と実行時に変数の値をその型と照合します。
変数に格納されている値が変数の静的型と異なる状況になることはありません。ほとんどの最新の静的に型付けされた言語と同様に、Dart は静的(コンパイル時)チェックと動的(実行時)チェックを組み合わせてこれを実現します。
たとえば、次の型エラーはコンパイル時に検出されます。
List<int> numbers = [1, 2, 3];
List<String> string = numbers;
List<int>
もList<String>
も互いのサブタイプではないため、Dart はこれを静的に除外します。
静的解析エラーの他の例と、その他のエラーの種類については、次のセクションを参照してください。
型エラーなし
#予期したエラーまたは警告が表示されない場合は、最新バージョンの Dart を使用していて、IDE またはエディターが正しく構成されていることを確認してください。
dart analyze
コマンドを使用して、コマンドラインでプログラムの解析を実行することもできます。
解析が期待通りに機能していることを確認するには、次のコードを 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
が未定義であると文句を言います。
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
に変換されます)ですが、コードはそれがCanvasElement
(context2D
を定義する)のサブタイプであると想定しています。canvas
フィールドはvar
として宣言されているため、Dart はcanvas
をElement
として推論できます。
明示的なダウンキャストを使用してこのエラーを修正できます。
var canvas = querySelector('canvas') as CanvasElement;
canvas.context2D.lineTo(x, y);
そうでない場合は、単一の型を使用できない状況ではdynamic
を使用します。
dynamic canvasOrImg = querySelector('canvas, img');
var width = canvasOrImg.width;
例2:省略された型パラメーターは型境界にデフォルトで設定されます
#Iterable
を拡張する境界付き型パラメーターを持つ次の**ジェネリッククラス**を考えてみましょう。
class C<T extends Iterable> {
final T collection;
C(this.collection);
}
次のコードは、このクラスの新しいインスタンスを作成し(型引数を省略して)、そのcollection
メンバーにアクセスします。
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
を指定するように変更すると、アナライザーはコンストラクタ引数の型ミスマッチを検出できます。適切な型のコンストラクタ引数(リストリテラルなど)を提供することで、エラーを修正します。
var c = C<List>([]).collection;
c.add(2);
無効なメソッドオーバーライド
#error - '...' isn't a valid override of '...' - invalid_override
これらのエラーは、通常、サブクラスが元のクラスのサブクラスを指定することでメソッドのパラメーター型を厳しくする場合に発生します。
例
#次の例では、add()
メソッドのパラメーターはint
型(num
のサブタイプ)であり、親クラスで使用されるパラメーター型です。
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
に渡される次のシナリオを考えてみましょう。
NumberAdder adder = MyAdder();
adder.add(1.2, 3.4);
オーバーライドが許可された場合、コードは実行時にエラーを発生させます。
修正:メソッドのパラメーター型を広げます
#サブクラスのメソッドは、スーパークラスのメソッドが受け取るすべてのオブジェクトを受け入れる必要があります。
サブクラスの型を広げることで例を修正します。
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
例
#次の例では、Subclass
はSuperclass<T>
を拡張しますが、型引数を指定していません。アナライザーはSubclass<dynamic>
を推論し、その結果、method(int)
で無効なオーバーライドエラーが発生します。
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
型を推論します。これにより、エラーが発生する可能性が高くなります。
サブクラスに型を指定することで、例を修正できます。
class Superclass<T> {
void method(T param) { ... }
}
class Subclass extends Superclass<int> {
@override
void method(int param) { ... }
}
コードでジェネリック型引数を指定するようにする、厳格な生の型モードでアナライザーを使用することを検討してください。プロジェクトのanalysis_options.yaml
ファイルで厳格な生の型を有効にする例を次に示します。
analyzer:
language:
strict-raw-types: true
アナライザーの動作のカスタマイズの詳細については、静的解析のカスタマイズを参照してください。
予期しないコレクション要素型
#error - A value of type '...' can't be assigned to a variable of type '...' - invalid_assignment
これは、単純な動的なコレクションを作成し、アナライザーが予期しない方法で型を推論した場合に発生することがあります。後で異なる型の値を追加すると、アナライザーは問題を報告します。
例
#次のコードは、いくつかの(String
、int
)ペアでマップを初期化します。アナライザーは、そのマップが<String, int>
型であると推論しますが、コードは<String, dynamic>
または<String, num>
のいずれかを想定しているようです。コードに(String
、double
)ペアを追加すると、アナライザーは文句を言います。
// 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>
と明示的に定義することで修正できます。
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()
呼び出しが最後でない場合に発生します。
例
#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()
呼び出しを移動します。
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?
などの他のトップタイプ)以外の型パラメータを使用すると、コンパイル時エラーが発生します。
例
#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
から明示的にキャストします。
#可能な場合は、型パラメータを追加することでこのエラーを回避します。
void filterValues<T>(bool Function(T) filter) {}
filterValues<String>((x) => x.contains('Hello'));
それ以外の場合は、キャストを使用します。
void filterValues(bool Function(dynamic) filter) {}
filterValues((x) => (x as String).contains('Hello'));
不正確な型推論
#まれに、Dartの型推論が、ジェネリックコンストラクタ呼び出しにおける関数リテラル引数について間違った型を推論することがあります。これは主にIterable.fold
に影響します。
例
#次のコードでは、型推論によりa
の型はNull
と推論されます。
var ints = [1, 2, 3];
var maximumOrNull = ints.fold(null, (a, b) => a == null || a < b ? b : a);
修正:適切な型を明示的な型引数として指定します。
#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
は競合するジェネリックインターフェースを持っています。一部のメンバに対する有効なオーバーライドの定義は不可能です。
abstract class C implements List<int>, Iterable<num> {}
修正:一貫性のあるジェネリックを使用するか、推移的なインターフェースの重複を避けます。
#abstract class C implements List<int> {}
ランタイムエラー
#このセクションで説明されているエラーは、ランタイムで報告されます。
無効なキャスト
#型安全性を確保するために、Dartは場合によってはランタイムチェックを挿入する必要があります。次のassumeStrings
メソッドを考えてみましょう。
void assumeStrings(dynamic objects) {
List<String> strings = objects; // Runtime downcast check
String string = strings[0]; // Expect a String value
}
strings
への代入は、dynamic
をList<String>
に暗黙的にダウンスキャストしています(as List<String>
と書いた場合と同じです)。そのため、ランタイムでobjects
に渡す値がList<String>
の場合、キャストは成功します。
それ以外の場合は、ランタイムでキャストが失敗します。
assumeStrings(<int>[1, 2, 3]);
Exception: type 'List<int>' is not a subtype of type 'List<String>'
修正:型を厳しくするか修正します。
#特に空の集合の場合、型の不足は、意図した型付きコレクションではなく、<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
などのIterable
実装によって提供されるcast()メソッドを使用できます。
こちらが推奨される解決策の例です。オブジェクトの型を厳しくします。
Map<String, dynamic> json = fetchFromExternalSource();
var names = json['names'] as List;
assumeStrings(names.cast<String>());
付録
#共変キーワード
#一部(めったに使用されない)コーディングパターンは、パラメータの型をサブタイプでオーバーライドすることで型を厳しくすることに依存していますが、これは無効です。この場合、covariant
キーワードを使用して、意図的にこれを行っていることをアナライザーに伝えることができます。これにより、静的エラーは削除され、代わりにランタイムで無効な引数の型がチェックされます。
以下は、covariant
の使用方法を示しています。
class Animal {
void chase(Animal x) { ... }
}
class Mouse extends Animal { ... }
class Cat extends Animal {
@override
void chase(covariant Mouse x) { ... }
}
この例ではサブタイプでcovariant
を使用していますが、covariant
キーワードはスーパークラスまたはサブクラスのメソッドのどちらにも配置できます。通常、スーパークラスのメソッドに配置するのが最適です。covariant
キーワードは単一のパラメータに適用され、セッターとフィールドでもサポートされています。
特に明記されていない限り、このサイトのドキュメントはDart 3.5.3を反映しています。ページ最終更新日:2024年7月24日。 ソースを表示 または 問題を報告する。