@@ -333,10 +333,6 @@ namespace ts {
333
333
/** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */
334
334
let apparentArgumentCount: number | undefined;
335
335
336
- // This object is reused for `checkOptionalExpression` return values to avoid frequent GC due to nursery object allocations.
337
- // This object represents a pool-size of 1.
338
- const pooledOptionalTypeResult: { isOptional: boolean, type: Type } = { isOptional: false, type: undefined! };
339
-
340
336
// for public members that accept a Node or one of its subtypes, we must guard against
341
337
// synthetic nodes created during transformations by calling `getParseTreeNode`.
342
338
// for most of these, we perform the guard only on `checker` to avoid any possible
@@ -10265,7 +10261,7 @@ namespace ts {
10265
10261
getReturnTypeFromAnnotation(signature.declaration!) ||
10266
10262
(nodeIsMissing((<FunctionLikeDeclaration>signature.declaration).body) ? anyType : getReturnTypeFromBody(<FunctionLikeDeclaration>signature.declaration));
10267
10263
if (signature.isOptionalCall) {
10268
- type = propagateOptionalTypeMarker (type, /*wasOptional*/ true );
10264
+ type = addOptionalTypeMarker (type);
10269
10265
}
10270
10266
if (!popTypeResolution()) {
10271
10267
if (signature.declaration) {
@@ -16677,51 +16673,50 @@ namespace ts {
16677
16673
}
16678
16674
16679
16675
function addOptionalTypeMarker(type: Type) {
16680
- return strictNullChecks ? getUnionType([type, optionalType]) : type;
16676
+ if (strictNullChecks) {
16677
+ if (isTypeAny(type)) return type;
16678
+ if (type.optionalTypeOfType) {
16679
+ // The type aleady has an optional type cache.
16680
+ return type.optionalTypeOfType;
16681
+ }
16682
+ if (type.flags & TypeFlags.Union && type.nonOptionalTypeOfType) {
16683
+ // The type *is* the type produced by marking a type with the optional type.
16684
+ return type;
16685
+ }
16686
+ type.optionalTypeOfType = getUnionType([type, optionalType]);
16687
+ type.optionalTypeOfType.nonOptionalTypeOfType = type;
16688
+ return type.optionalTypeOfType;
16689
+ }
16690
+ return type;
16681
16691
}
16682
16692
16683
16693
function removeOptionalTypeMarker(type: Type): Type {
16684
- return strictNullChecks ? filterType(type, t => t !== optionalType) : type;
16694
+ if (strictNullChecks) {
16695
+ if (type.flags & TypeFlags.Union && type.nonOptionalTypeOfType) {
16696
+ return type.nonOptionalTypeOfType;
16697
+ }
16698
+ return filterType(type, t => t !== optionalType);
16699
+ }
16700
+ return type;
16685
16701
}
16686
16702
16687
16703
function propagateOptionalTypeMarker(type: Type, wasOptional: boolean) {
16688
16704
return wasOptional ? addOptionalTypeMarker(type) : type;
16689
16705
}
16690
16706
16691
- function createPooledOptionalTypeResult(isOptional: boolean, type: Type) {
16692
- pooledOptionalTypeResult.isOptional = isOptional;
16693
- pooledOptionalTypeResult.type = type;
16694
- return pooledOptionalTypeResult;
16695
- }
16696
-
16697
16707
function checkOptionalExpression(
16698
- parent: PropertyAccessExpression | QualifiedName | ElementAccessExpression | CallExpression,
16699
- expression: Expression | QualifiedName,
16708
+ expression: Expression,
16700
16709
nullDiagnostic?: DiagnosticMessage,
16701
16710
undefinedDiagnostic?: DiagnosticMessage,
16702
16711
nullOrUndefinedDiagnostic?: DiagnosticMessage,
16703
16712
) {
16704
- let isOptional = false;
16705
- let type = checkExpression(expression);
16706
- if (isOptionalChain(parent)) {
16707
- if (parent.questionDotToken) {
16708
- // If we have a questionDotToken then we are an OptionalExpression and should remove `null` and
16709
- // `undefined` from the type and add the optionalType to the result, if needed.
16710
- isOptional = isNullableType(type);
16711
- return createPooledOptionalTypeResult(isOptional, isOptional ? getNonNullableType(type) : type);
16712
- }
16713
-
16714
- // If we do not have a questionDotToken, then we are an OptionalChain and we remove the optionalType and
16715
- // indicate whether we need to add optionalType back into the result.
16716
- const nonOptionalType = removeOptionalTypeMarker(type);
16717
- if (nonOptionalType !== type) {
16718
- isOptional = true;
16719
- type = nonOptionalType;
16720
- }
16721
- }
16722
-
16723
- type = checkNonNullType(type, expression, nullDiagnostic, undefinedDiagnostic, nullOrUndefinedDiagnostic);
16724
- return createPooledOptionalTypeResult(isOptional, type);
16713
+ const exprType = checkExpression(expression);
16714
+ const nonOptionalType =
16715
+ isOptionalExpression(expression) ? getNonNullableType(exprType) : // 'a' in 'a?.b.c'
16716
+ isOptionalChain(expression) ? removeOptionalTypeMarker(exprType) : // 'a?.b' in 'a?.b.c'
16717
+ exprType;
16718
+ const nonNullType = checkNonNullType(nonOptionalType, expression, nullDiagnostic, undefinedDiagnostic, nullOrUndefinedDiagnostic);
16719
+ return exprType !== nonOptionalType ? addOptionalTypeMarker(nonNullType) : nonNullType;
16725
16720
}
16726
16721
16727
16722
/**
@@ -18756,7 +18751,8 @@ namespace ts {
18756
18751
// circularities in control flow analysis, we use getTypeOfDottedName when resolving the call
18757
18752
// target expression of an assertion.
18758
18753
const funcType = node.parent.kind === SyntaxKind.ExpressionStatement ? getTypeOfDottedName(node.expression, /*diagnostic*/ undefined) :
18759
- node.expression.kind !== SyntaxKind.SuperKeyword ? checkOptionalExpression(node, node.expression).type :
18754
+ node.expression.kind !== SyntaxKind.SuperKeyword ? isOptionalChain(node) ? removeOptionalTypeMarker(checkOptionalExpression(node.expression)) :
18755
+ checkNonNullExpression(node.expression) :
18760
18756
undefined;
18761
18757
const signatures = getSignaturesOfType(funcType && getApparentType(funcType) || unknownType, SignatureKind.Call);
18762
18758
const candidate = signatures.length === 1 && !signatures[0].typeParameters ? signatures[0] :
@@ -19718,7 +19714,7 @@ namespace ts {
19718
19714
// will be a subtype or the same type as the argument.
19719
19715
function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type {
19720
19716
// for `a?.b`, we emulate a synthetic `a !== null && a !== undefined` condition for `a`
19721
- if (isOptionalChainRoot (expr.parent ) ||
19717
+ if (isOptionalExpression (expr) ||
19722
19718
isBinaryExpression(expr.parent) && expr.parent.operatorToken.kind === SyntaxKind.QuestionQuestionToken && expr.parent.left === expr) {
19723
19719
return narrowTypeByOptionality(type, expr, assumeTrue);
19724
19720
}
@@ -22672,11 +22668,18 @@ namespace ts {
22672
22668
}
22673
22669
22674
22670
function checkPropertyAccessExpression(node: PropertyAccessExpression) {
22675
- return checkPropertyAccessExpressionOrQualifiedName(node, node.expression, node.name);
22671
+ return isPropertyAccessChain(node) ? checkPropertyAccessChain(node) :
22672
+ checkPropertyAccessExpressionOrQualifiedName(node, node.expression, checkNonNullExpression(node.expression), node.name);
22673
+ }
22674
+
22675
+ function checkPropertyAccessChain(node: PropertyAccessChain) {
22676
+ const optionalType = checkOptionalExpression(node.expression);
22677
+ const nonOptionalType = removeOptionalTypeMarker(optionalType);
22678
+ return propagateOptionalTypeMarker(checkPropertyAccessExpressionOrQualifiedName(node, node.expression, nonOptionalType, node.name), nonOptionalType !== optionalType);
22676
22679
}
22677
22680
22678
22681
function checkQualifiedName(node: QualifiedName) {
22679
- return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
22682
+ return checkPropertyAccessExpressionOrQualifiedName(node, node.left, checkNonNullExpression(node.left), node.right);
22680
22683
}
22681
22684
22682
22685
function isMethodAccessForCall(node: Node) {
@@ -22686,8 +22689,7 @@ namespace ts {
22686
22689
return isCallOrNewExpression(node.parent) && node.parent.expression === node;
22687
22690
}
22688
22691
22689
- function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
22690
- const { isOptional, type: leftType } = checkOptionalExpression(node, left);
22692
+ function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, leftType: Type, right: Identifier) {
22691
22693
const parentSymbol = getNodeLinks(left).resolvedSymbol;
22692
22694
const assignmentKind = getAssignmentTargetKind(node);
22693
22695
const apparentType = getApparentType(assignmentKind !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(leftType) : leftType);
@@ -22741,7 +22743,7 @@ namespace ts {
22741
22743
}
22742
22744
propType = getConstraintForLocation(getTypeOfSymbol(prop), node);
22743
22745
}
22744
- return propagateOptionalTypeMarker( getFlowTypeOfAccessExpression(node, prop, propType, right), isOptional );
22746
+ return getFlowTypeOfAccessExpression(node, prop, propType, right);
22745
22747
}
22746
22748
22747
22749
function getFlowTypeOfAccessExpression(node: ElementAccessExpression | PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, propType: Type, errorNode: Node) {
@@ -23098,7 +23100,17 @@ namespace ts {
23098
23100
}
23099
23101
23100
23102
function checkIndexedAccess(node: ElementAccessExpression): Type {
23101
- const { isOptional, type: exprType } = checkOptionalExpression(node, node.expression);
23103
+ return isElementAccessChain(node) ? checkElementAccessChain(node) :
23104
+ checkElementAccessExpression(node, checkNonNullExpression(node.expression));
23105
+ }
23106
+
23107
+ function checkElementAccessChain(node: ElementAccessChain): Type {
23108
+ const optionalType = checkOptionalExpression(node.expression);
23109
+ const nonOptionalType = removeOptionalTypeMarker(optionalType);
23110
+ return propagateOptionalTypeMarker(checkElementAccessExpression(node, nonOptionalType), nonOptionalType !== optionalType);
23111
+ }
23112
+
23113
+ function checkElementAccessExpression(node: ElementAccessExpression, exprType: Type): Type {
23102
23114
const objectType = getAssignmentTargetKind(node) !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(exprType) : exprType;
23103
23115
const indexExpression = node.argumentExpression;
23104
23116
const indexType = checkExpression(indexExpression);
@@ -23117,7 +23129,7 @@ namespace ts {
23117
23129
AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) :
23118
23130
AccessFlags.None;
23119
23131
const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags) || errorType;
23120
- return propagateOptionalTypeMarker( checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node), isOptional );
23132
+ return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node);
23121
23133
}
23122
23134
23123
23135
function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean {
@@ -24284,34 +24296,53 @@ namespace ts {
24284
24296
}
24285
24297
24286
24298
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
24299
+ if (isOptionalChain(node)) {
24300
+ return resolveCallChain(node, candidatesOutArray, checkMode);
24301
+ }
24287
24302
if (node.expression.kind === SyntaxKind.SuperKeyword) {
24288
- const superType = checkSuperExpression(node.expression);
24289
- if (isTypeAny(superType)) {
24290
- for (const arg of node.arguments) {
24291
- checkExpression(arg); // Still visit arguments so they get marked for visibility, etc
24292
- }
24293
- return anySignature;
24294
- }
24295
- if (superType !== errorType) {
24296
- // In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated
24297
- // with the type arguments specified in the extends clause.
24298
- const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!);
24299
- if (baseTypeNode) {
24300
- const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode);
24301
- return resolveCall(node, baseConstructors, candidatesOutArray, checkMode, /*isOptional*/ false);
24302
- }
24303
- }
24304
- return resolveUntypedCall(node);
24303
+ return resolveSuperCall(node as SuperCall, candidatesOutArray, checkMode);
24305
24304
}
24305
+ const funcType = checkNonNullExpression(
24306
+ node.expression,
24307
+ Diagnostics.Cannot_invoke_an_object_which_is_possibly_null,
24308
+ Diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined,
24309
+ Diagnostics.Cannot_invoke_an_object_which_is_possibly_null_or_undefined
24310
+ );
24311
+ return resolveCallExpressionWorker(node, funcType, candidatesOutArray, checkMode, /*isOptionalCall*/ false);
24312
+ }
24306
24313
24307
- const { isOptional, type: funcType } = checkOptionalExpression(
24308
- node,
24314
+ function resolveCallChain(node: CallChain, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode) {
24315
+ const optionalType = checkOptionalExpression(
24309
24316
node.expression,
24310
24317
Diagnostics.Cannot_invoke_an_object_which_is_possibly_null,
24311
24318
Diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined,
24312
24319
Diagnostics.Cannot_invoke_an_object_which_is_possibly_null_or_undefined
24313
24320
);
24321
+ const nonOptionalType = removeOptionalTypeMarker(optionalType);
24322
+ return resolveCallExpressionWorker(node, nonOptionalType, candidatesOutArray, checkMode, nonOptionalType !== optionalType);
24323
+ }
24324
+
24325
+ function resolveSuperCall(node: SuperCall, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode) {
24326
+ const superType = checkSuperExpression(node.expression);
24327
+ if (isTypeAny(superType)) {
24328
+ for (const arg of node.arguments) {
24329
+ checkExpression(arg); // Still visit arguments so they get marked for visibility, etc
24330
+ }
24331
+ return anySignature;
24332
+ }
24333
+ if (superType !== errorType) {
24334
+ // In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated
24335
+ // with the type arguments specified in the extends clause.
24336
+ const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!);
24337
+ if (baseTypeNode) {
24338
+ const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode);
24339
+ return resolveCall(node, baseConstructors, candidatesOutArray, checkMode, /*isOptional*/ false);
24340
+ }
24341
+ }
24342
+ return resolveUntypedCall(node);
24343
+ }
24314
24344
24345
+ function resolveCallExpressionWorker(node: CallExpression, funcType: Type, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, isOptionalCall: boolean): Signature {
24315
24346
if (funcType === silentNeverType) {
24316
24347
return silentNeverSignature;
24317
24348
}
@@ -24381,7 +24412,7 @@ namespace ts {
24381
24412
return resolveErrorCall(node);
24382
24413
}
24383
24414
24384
- return resolveCall(node, callSignatures, candidatesOutArray, checkMode, isOptional );
24415
+ return resolveCall(node, callSignatures, candidatesOutArray, checkMode, isOptionalCall );
24385
24416
}
24386
24417
24387
24418
function isGenericFunctionReturningFunction(signature: Signature) {
@@ -27265,6 +27296,20 @@ namespace ts {
27265
27296
}
27266
27297
}
27267
27298
27299
+ function getReturnTypeOfSingleNonGenericCallSignatureOfCallChain(node: CallChain) {
27300
+ const optionalType = checkOptionalExpression(node.expression);
27301
+ const nonOptionalType = removeOptionalTypeMarker(optionalType);
27302
+ const returnType = getReturnTypeOfSingleNonGenericCallSignature(nonOptionalType);
27303
+ return returnType && propagateOptionalTypeMarker(returnType, nonOptionalType !== optionalType);
27304
+ }
27305
+
27306
+ function getReturnTypeOfSingleNonGenericCallSignature(funcType: Type) {
27307
+ const signature = getSingleCallSignature(funcType);
27308
+ if (signature && !signature.typeParameters) {
27309
+ return getReturnTypeOfSignature(signature);
27310
+ }
27311
+ }
27312
+
27268
27313
/**
27269
27314
* Returns the type of an expression. Unlike checkExpression, this function is simply concerned
27270
27315
* with computing the type and may not fully check all contained sub-expressions for errors.
@@ -27276,10 +27321,11 @@ namespace ts {
27276
27321
// Optimize for the common case of a call to a function with a single non-generic call
27277
27322
// signature where we can just fetch the return type without checking the arguments.
27278
27323
if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) {
27279
- const { isOptional, type: funcType } = checkOptionalExpression(expr, expr.expression);
27280
- const signature = getSingleCallSignature(funcType);
27281
- if (signature && !signature.typeParameters) {
27282
- return propagateOptionalTypeMarker(getReturnTypeOfSignature(signature), isOptional);
27324
+ const type = isCallChain(expr) ?
27325
+ getReturnTypeOfSingleNonGenericCallSignatureOfCallChain(expr) :
27326
+ getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression));
27327
+ if (type) {
27328
+ return type;
27283
27329
}
27284
27330
}
27285
27331
else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) {
0 commit comments