Strip nullable types from 'this' type in inference with optional chain calls (#42536)
* Properly strip nullable types from this type in optional chain calls * Add regression test
This commit is contained in:
parent
cce4bfbc7c
commit
e2318217fb
|
@ -27160,6 +27160,16 @@ namespace ts {
|
|||
return getInferredTypes(context);
|
||||
}
|
||||
|
||||
function getThisArgumentType(thisArgumentNode: LeftHandSideExpression | undefined) {
|
||||
if (!thisArgumentNode) {
|
||||
return voidType;
|
||||
}
|
||||
const thisArgumentType = checkExpression(thisArgumentNode);
|
||||
return isOptionalChainRoot(thisArgumentNode.parent) ? getNonNullableType(thisArgumentType) :
|
||||
isOptionalChain(thisArgumentNode.parent) ? removeOptionalTypeMarker(thisArgumentType) :
|
||||
thisArgumentType;
|
||||
}
|
||||
|
||||
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: readonly Expression[], checkMode: CheckMode, context: InferenceContext): Type[] {
|
||||
if (isJsxOpeningLikeElement(node)) {
|
||||
return inferJsxTypeArguments(node, signature, checkMode, context);
|
||||
|
@ -27215,8 +27225,7 @@ namespace ts {
|
|||
const thisType = getThisTypeOfSignature(signature);
|
||||
if (thisType) {
|
||||
const thisArgumentNode = getThisArgumentOfCall(node);
|
||||
const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
|
||||
inferTypes(context.inferences, thisArgumentType, thisType);
|
||||
inferTypes(context.inferences, getThisArgumentType(thisArgumentNode), thisType);
|
||||
}
|
||||
|
||||
for (let i = 0; i < argCount; i++) {
|
||||
|
@ -27457,20 +27466,7 @@ namespace ts {
|
|||
// If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible.
|
||||
// If the expression is a new expression, then the check is skipped.
|
||||
const thisArgumentNode = getThisArgumentOfCall(node);
|
||||
let thisArgumentType: Type;
|
||||
if (thisArgumentNode) {
|
||||
thisArgumentType = checkExpression(thisArgumentNode);
|
||||
if (isOptionalChainRoot(thisArgumentNode.parent)) {
|
||||
thisArgumentType = getNonNullableType(thisArgumentType);
|
||||
}
|
||||
else if (isOptionalChain(thisArgumentNode.parent)) {
|
||||
thisArgumentType = removeOptionalTypeMarker(thisArgumentType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
thisArgumentType = voidType;
|
||||
}
|
||||
|
||||
const thisArgumentType = getThisArgumentType(thisArgumentNode);
|
||||
const errorNode = reportErrors ? (thisArgumentNode || node) : undefined;
|
||||
const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1;
|
||||
if (!checkTypeRelatedTo(thisArgumentType, thisType, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer)) {
|
||||
|
|
25
tests/baselines/reference/callChainInference.js
Normal file
25
tests/baselines/reference/callChainInference.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
//// [callChainInference.ts]
|
||||
// Repro from #42404
|
||||
|
||||
interface Y {
|
||||
foo<T>(this: T, arg: keyof T): void;
|
||||
a: number;
|
||||
b: string;
|
||||
}
|
||||
|
||||
declare const value: Y | undefined;
|
||||
|
||||
if (value) {
|
||||
value?.foo("a");
|
||||
}
|
||||
|
||||
value?.foo("a");
|
||||
|
||||
|
||||
//// [callChainInference.js]
|
||||
"use strict";
|
||||
// Repro from #42404
|
||||
if (value) {
|
||||
value === null || value === void 0 ? void 0 : value.foo("a");
|
||||
}
|
||||
value === null || value === void 0 ? void 0 : value.foo("a");
|
39
tests/baselines/reference/callChainInference.symbols
Normal file
39
tests/baselines/reference/callChainInference.symbols
Normal file
|
@ -0,0 +1,39 @@
|
|||
=== tests/cases/conformance/expressions/optionalChaining/callChain/callChainInference.ts ===
|
||||
// Repro from #42404
|
||||
|
||||
interface Y {
|
||||
>Y : Symbol(Y, Decl(callChainInference.ts, 0, 0))
|
||||
|
||||
foo<T>(this: T, arg: keyof T): void;
|
||||
>foo : Symbol(Y.foo, Decl(callChainInference.ts, 2, 13))
|
||||
>T : Symbol(T, Decl(callChainInference.ts, 3, 8))
|
||||
>this : Symbol(this, Decl(callChainInference.ts, 3, 11))
|
||||
>T : Symbol(T, Decl(callChainInference.ts, 3, 8))
|
||||
>arg : Symbol(arg, Decl(callChainInference.ts, 3, 19))
|
||||
>T : Symbol(T, Decl(callChainInference.ts, 3, 8))
|
||||
|
||||
a: number;
|
||||
>a : Symbol(Y.a, Decl(callChainInference.ts, 3, 40))
|
||||
|
||||
b: string;
|
||||
>b : Symbol(Y.b, Decl(callChainInference.ts, 4, 14))
|
||||
}
|
||||
|
||||
declare const value: Y | undefined;
|
||||
>value : Symbol(value, Decl(callChainInference.ts, 8, 13))
|
||||
>Y : Symbol(Y, Decl(callChainInference.ts, 0, 0))
|
||||
|
||||
if (value) {
|
||||
>value : Symbol(value, Decl(callChainInference.ts, 8, 13))
|
||||
|
||||
value?.foo("a");
|
||||
>value?.foo : Symbol(Y.foo, Decl(callChainInference.ts, 2, 13))
|
||||
>value : Symbol(value, Decl(callChainInference.ts, 8, 13))
|
||||
>foo : Symbol(Y.foo, Decl(callChainInference.ts, 2, 13))
|
||||
}
|
||||
|
||||
value?.foo("a");
|
||||
>value?.foo : Symbol(Y.foo, Decl(callChainInference.ts, 2, 13))
|
||||
>value : Symbol(value, Decl(callChainInference.ts, 8, 13))
|
||||
>foo : Symbol(Y.foo, Decl(callChainInference.ts, 2, 13))
|
||||
|
37
tests/baselines/reference/callChainInference.types
Normal file
37
tests/baselines/reference/callChainInference.types
Normal file
|
@ -0,0 +1,37 @@
|
|||
=== tests/cases/conformance/expressions/optionalChaining/callChain/callChainInference.ts ===
|
||||
// Repro from #42404
|
||||
|
||||
interface Y {
|
||||
foo<T>(this: T, arg: keyof T): void;
|
||||
>foo : <T>(this: T, arg: keyof T) => void
|
||||
>this : T
|
||||
>arg : keyof T
|
||||
|
||||
a: number;
|
||||
>a : number
|
||||
|
||||
b: string;
|
||||
>b : string
|
||||
}
|
||||
|
||||
declare const value: Y | undefined;
|
||||
>value : Y | undefined
|
||||
|
||||
if (value) {
|
||||
>value : Y | undefined
|
||||
|
||||
value?.foo("a");
|
||||
>value?.foo("a") : void
|
||||
>value?.foo : <T>(this: T, arg: keyof T) => void
|
||||
>value : Y
|
||||
>foo : <T>(this: T, arg: keyof T) => void
|
||||
>"a" : "a"
|
||||
}
|
||||
|
||||
value?.foo("a");
|
||||
>value?.foo("a") : void | undefined
|
||||
>value?.foo : (<T>(this: T, arg: keyof T) => void) | undefined
|
||||
>value : Y | undefined
|
||||
>foo : (<T>(this: T, arg: keyof T) => void) | undefined
|
||||
>"a" : "a"
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// @strict: true
|
||||
|
||||
// Repro from #42404
|
||||
|
||||
interface Y {
|
||||
foo<T>(this: T, arg: keyof T): void;
|
||||
a: number;
|
||||
b: string;
|
||||
}
|
||||
|
||||
declare const value: Y | undefined;
|
||||
|
||||
if (value) {
|
||||
value?.foo("a");
|
||||
}
|
||||
|
||||
value?.foo("a");
|
Loading…
Reference in a new issue