arg is Tの推論を目指す
やりたいこと: contextual typeがある場合に関数式の返り値の型としてarg is Tが推論される。
現在の挙動: contextual typeの返り値型がarg is Tだとしても無視されて boolean
になる。
-
contextuallyCheckFunctionExpressionOrObjectLiteralMethod
に関数式のチェックがある
-
interface Signature
の定義を見るとresolvedReturnType
とは別にresolvedTypePredicate
が存在している
-
getContextualSignature
→getApparentTypeOfContextualType
ここで contextual type が来ているけどSignatureを復元できるか?- よく考ええたら関数は
callSignatures: readonly Signature[]
を持つオブジェクト型だからまあできる - console.logしてみたら
resolvedTypePredicate
を持つ型がcontextualSignature
として得られていた -
instantiatedContextualSignature
にも引き継がれている
- よく考ええたら関数は
contextuallyCheckFunctionExpressionOrObjectLiteralMethod
内で contextualSignature
から推論された関数型へtype predicateを引き継がせるコード。(これに対する推論が何もないので型変数がリークしたりする)
const contexntualSignatureTypePredicate = getTypePredicateOfSignature(contextualSignature);
if (!signature.resolvedTypePredicate && contexntualSignatureTypePredicate) {
signature.resolvedTypePredicate = contexntualSignatureTypePredicate;
}
型変数に推論を働かせるための手がかり: この辺りを追うとよさそう
resolveCall
→ chooseOverload
→ createInferenceContext
→ createInferenceContextWorker
→ makeFixingMapperForContext
→ getInfereedType
→ instantiateType
const nums = [1, 2, null, 4, 5, null, 7, 8];
const filtered = nums.filter(x => x !== null);
const a: number[] = filtered;
この時点で inferTypeArguments
により number | null
が推論されているがそれが反映されず S
のままになっている。
function id <S>(func: (arg: unknown) => arg is S): ((arg: unknown) => arg is S) {return func}
const func = id((x) => typeof x === 'number');
これのfunc
に対応するSignatureを調べると、resolvedTypePredicate
に arg is S
が入っているがSignatureが target
や mapper
を持たない。
-
getResolvedSignature
が返したSignatureはmapperとしてS => S
を持っているように見える。ここがおかしい。(本来はS => unknown
のように型変数が解決されているべき)
-
interfaceContext.inferences[0].candidates
にS
がそのまま入るタイミングがある。-
inferTypeArguments
→inferTypes
(argに対するforループのところ) →inferFromTypes
→inferFromObjectTypes
→inferFromSignatures(source, target, SignatureKind.Call);
-
source
=(x) => x is S
-
target
=(arg) => arg is S
-
-
最終的に
inferFromTypes( <S>, <S> )
になってここでS = S
が生成される。これが正しい挙動かどうか要調査
-
同じような推論過程になると期待される次のコードの場合はどうなるか?
function id<S>(func: (arg: S) => S): (arg: S) => S { return func }
const func = id((x) => x);
→より早いタイミングでsourceが unknown
に解決されている
-
inferTypeArguments
から呼び出されるcheckExpressionWithContextualType
の返り値の時点ですでに(x) => unknown
になっている - 関数引数の
x
のsymbol.links.type
にすでにunknown
が入っている。どこから?