TypeScriptバージョン: 2.0.8
コード
// test.ts
interface Type {
type: number;
}
interface TypeExt extends Type {
arr: Type[];
}
const guard = (arg: Type): arg is TypeExt => arg.type === 1;
const otherFunc = (arg1: Type, arg2: TypeExt): void => {};
export function y(arg: Type): void {
if (guard(arg)) {
for (const ITEM/* error is here */ of arg.arr) {
if (otherFunc(ITEM, arg)) {
}
}
}
}
cmdでコンパイル: tsc --noImplicitAny test.ts
予想される行動:
エラーなし
実際の動作:
test.ts(14,16): error TS7022: 'ITEM' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
これは制御フロー分析の問題です。 以下のためのタイプ推測するためにITEM
、私たちは、の制御フロータイプを把握する必要がありますarg.arr
。 つまり、 arg
タイプに影響を与える構成を調べる必要があります。 これには、 otherFunc
の呼び出しが含まれます。これは、ユーザー定義型の述語である可能性があります。 それが型述語であるかどうかを判断するために、呼び出し式を解決します。これには、引数式の解決が含まれ、 ITEM
型を知っている必要があります。 これにより、解決できない循環が作成されるため、暗黙のany
タイプにフォールバックします。
これを修正するには、型述語認識ロジックで最初に関数オブジェクトの型を解決し、すべての呼び出しシグネチャを調べます。 それらのいずれもユーザー定義型の述語ではない場合、呼び出し引数式を解決せずに早期にベイルアウトできます。これにより、循環性が回避されます。
一方、 otherFunc
呼び出しでarg
に括弧を追加するだけで、循環性を破ることができます。 つまり、呼び出しをotherFunc(ITEM, (arg))
に変更します。 これにより、制御フロー・アナライザーは、呼び出しをユーザー定義型の述語呼び出しの可能性があると見なしなくなります。
@ahejlsberg
括弧の回避策をありがとうございます。
これを理解したい。 PRは受け入れられますね。
@arusakovマスターで修正しました!
最も参考になるコメント
これは制御フロー分析の問題です。 以下のためのタイプ推測するために
ITEM
、私たちは、の制御フロータイプを把握する必要がありますarg.arr
。 つまり、arg
タイプに影響を与える構成を調べる必要があります。 これには、otherFunc
の呼び出しが含まれます。これは、ユーザー定義型の述語である可能性があります。 それが型述語であるかどうかを判断するために、呼び出し式を解決します。これには、引数式の解決が含まれ、ITEM
型を知っている必要があります。 これにより、解決できない循環が作成されるため、暗黙のany
タイプにフォールバックします。これを修正するには、型述語認識ロジックで最初に関数オブジェクトの型を解決し、すべての呼び出しシグネチャを調べます。 それらのいずれもユーザー定義型の述語ではない場合、呼び出し引数式を解決せずに早期にベイルアウトできます。これにより、循環性が回避されます。
一方、
otherFunc
呼び出しでarg
に括弧を追加するだけで、循環性を破ることができます。 つまり、呼び出しをotherFunc(ITEM, (arg))
に変更します。 これにより、制御フロー・アナライザーは、呼び出しをユーザー定義型の述語呼び出しの可能性があると見なしなくなります。