diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d1ebe307294e7..cb569c2f22adc 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -797,6 +797,10 @@ namespace ts { case SyntaxKind.VariableDeclaration: bindVariableDeclarationFlow(node); break; + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + bindAccessExpressionFlow(node); + break; case SyntaxKind.CallExpression: bindCallExpressionFlow(node); break; @@ -941,7 +945,9 @@ namespace ts { } if (expression.kind === SyntaxKind.TrueKeyword && flags & FlowFlags.FalseCondition || expression.kind === SyntaxKind.FalseKeyword && flags & FlowFlags.TrueCondition) { - return unreachableFlow; + if (!isOptionalChainRoot(expression.parent)) { + return unreachableFlow; + } } if (!isNarrowingExpression(expression)) { return antecedent; @@ -1015,23 +1021,28 @@ namespace ts { } function isTopLevelLogicalExpression(node: Node): boolean { - while (node.parent.kind === SyntaxKind.ParenthesizedExpression || - node.parent.kind === SyntaxKind.PrefixUnaryExpression && - (node.parent).operator === SyntaxKind.ExclamationToken) { + while (isParenthesizedExpression(node.parent) || + isPrefixUnaryExpression(node.parent) && node.parent.operator === SyntaxKind.ExclamationToken) { node = node.parent; } - return !isStatementCondition(node) && !isLogicalExpression(node.parent); + return !isStatementCondition(node) && + !isLogicalExpression(node.parent) && + !(isOptionalChain(node.parent) && node.parent.expression === node); } - function bindCondition(node: Expression | undefined, trueTarget: FlowLabel, falseTarget: FlowLabel) { - const saveTrueTarget = currentTrueTarget; - const saveFalseTarget = currentFalseTarget; + function doWithConditionalBranches(action: (value: T) => void, value: T, trueTarget: FlowLabel, falseTarget: FlowLabel) { + const savedTrueTarget = currentTrueTarget; + const savedFalseTarget = currentFalseTarget; currentTrueTarget = trueTarget; currentFalseTarget = falseTarget; - bind(node); - currentTrueTarget = saveTrueTarget; - currentFalseTarget = saveFalseTarget; - if (!node || !isLogicalExpression(node)) { + action(value); + currentTrueTarget = savedTrueTarget; + currentFalseTarget = savedFalseTarget; + } + + function bindCondition(node: Expression | undefined, trueTarget: FlowLabel, falseTarget: FlowLabel) { + doWithConditionalBranches(bind, node, trueTarget, falseTarget); + if (!node || !isLogicalExpression(node) && !(isOptionalChain(node) && isOutermostOptionalChain(node))) { addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); } @@ -1536,22 +1547,96 @@ namespace ts { } } - function bindCallExpressionFlow(node: CallExpression) { - // If the target of the call expression is a function expression or arrow function we have - // an immediately invoked function expression (IIFE). Initialize the flowNode property to - // the current control flow (which includes evaluation of the IIFE arguments). - let expr: Expression = node.expression; - while (expr.kind === SyntaxKind.ParenthesizedExpression) { - expr = (expr).expression; - } - if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) { - bindEach(node.typeArguments); - bindEach(node.arguments); - bind(node.expression); + function isOutermostOptionalChain(node: OptionalChain) { + return !isOptionalChain(node.parent) || isOptionalChainRoot(node.parent) || node !== node.parent.expression; + } + + function bindOptionalExpression(node: Expression, trueTarget: FlowLabel, falseTarget: FlowLabel) { + doWithConditionalBranches(bind, node, trueTarget, falseTarget); + if (!isOptionalChain(node) || isOutermostOptionalChain(node)) { + addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); + addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); + } + } + + function bindOptionalChainRest(node: OptionalChain) { + bind(node.questionDotToken); + switch (node.kind) { + case SyntaxKind.PropertyAccessExpression: + bind(node.name); + break; + case SyntaxKind.ElementAccessExpression: + bind(node.argumentExpression); + break; + case SyntaxKind.CallExpression: + bindEach(node.typeArguments); + bindEach(node.arguments); + break; + } + } + + function bindOptionalChain(node: OptionalChain, trueTarget: FlowLabel, falseTarget: FlowLabel) { + // For an optional chain, we emulate the behavior of a logical expression: + // + // a?.b -> a && a.b + // a?.b.c -> a && a.b.c + // a?.b?.c -> a && a.b && a.b.c + // a?.[x = 1] -> a && a[x = 1] + // + // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`) + // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest + // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost + // chain node. We then treat the entire node as the right side of the expression. + const preChainLabel = node.questionDotToken ? createBranchLabel() : undefined; + bindOptionalExpression(node.expression, preChainLabel || trueTarget, falseTarget); + if (preChainLabel) { + currentFlow = finishFlowLabel(preChainLabel); + } + doWithConditionalBranches(bindOptionalChainRest, node, trueTarget, falseTarget); + if (isOutermostOptionalChain(node)) { + addAntecedent(trueTarget, createFlowCondition(FlowFlags.TrueCondition, currentFlow, node)); + addAntecedent(falseTarget, createFlowCondition(FlowFlags.FalseCondition, currentFlow, node)); + } + } + + function bindOptionalChainFlow(node: OptionalChain) { + if (isTopLevelLogicalExpression(node)) { + const postExpressionLabel = createBranchLabel(); + bindOptionalChain(node, postExpressionLabel, postExpressionLabel); + currentFlow = finishFlowLabel(postExpressionLabel); + } + else { + bindOptionalChain(node, currentTrueTarget!, currentFalseTarget!); + } + } + + function bindAccessExpressionFlow(node: AccessExpression) { + if (isOptionalChain(node)) { + bindOptionalChainFlow(node); } else { bindEachChild(node); } + } + + function bindCallExpressionFlow(node: CallExpression) { + if (isOptionalChain(node)) { + bindOptionalChainFlow(node); + } + else { + // If the target of the call expression is a function expression or arrow function we have + // an immediately invoked function expression (IIFE). Initialize the flowNode property to + // the current control flow (which includes evaluation of the IIFE arguments). + const expr = skipParentheses(node.expression); + if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) { + bindEach(node.typeArguments); + bindEach(node.arguments); + bind(node.expression); + } + else { + bindEachChild(node); + } + } if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { const propertyAccess = node.expression; if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) { @@ -3297,6 +3382,10 @@ namespace ts { const callee = skipOuterExpressions(node.expression); const expression = node.expression; + if (node.flags & NodeFlags.OptionalChain) { + transformFlags |= TransformFlags.ContainsESNext; + } + if (node.typeArguments) { transformFlags |= TransformFlags.AssertTypeScript; } @@ -3692,6 +3781,10 @@ namespace ts { function computePropertyAccess(node: PropertyAccessExpression, subtreeFlags: TransformFlags) { let transformFlags = subtreeFlags; + if (node.flags & NodeFlags.OptionalChain) { + transformFlags |= TransformFlags.ContainsESNext; + } + // If a PropertyAccessExpression starts with a super keyword, then it is // ES6 syntax, and requires a lexical `this` binding. if (node.expression.kind === SyntaxKind.SuperKeyword) { @@ -3707,6 +3800,10 @@ namespace ts { function computeElementAccess(node: ElementAccessExpression, subtreeFlags: TransformFlags) { let transformFlags = subtreeFlags; + if (node.flags & NodeFlags.OptionalChain) { + transformFlags |= TransformFlags.ContainsESNext; + } + // If an ElementAccessExpression starts with a super keyword, then it is // ES6 syntax, and requires a lexical `this` binding. if (node.expression.kind === SyntaxKind.SuperKeyword) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 97e08e7bccfe4..2c9f23f43bcec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -333,6 +333,10 @@ namespace ts { /** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */ let apparentArgumentCount: number | undefined; + // This object is reused for `checkOptionalExpression` return values to avoid frequent GC due to nursery object allocations. + // This object represents a pool-size of 1. + const pooledOptionalTypeResult: { isOptional: boolean, type: Type } = { isOptional: false, type: undefined! }; + // for public members that accept a Node or one of its subtypes, we must guard against // synthetic nodes created during transformations by calling `getParseTreeNode`. // for most of these, we perform the guard only on `checker` to avoid any possible @@ -380,8 +384,10 @@ namespace ts { getParameterType: getTypeAtPosition, getPromisedTypeOfPromise, getReturnTypeOfSignature, + isNullableType, getNullableType, getNonNullableType, + getNonOptionalType: removeOptionalTypeMarker, getTypeArguments, typeToTypeNode: nodeBuilder.typeToTypeNode, indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration, @@ -546,6 +552,7 @@ namespace ts { getNullType: () => nullType, getESSymbolType: () => esSymbolType, getNeverType: () => neverType, + getOptionalType: () => optionalType, isSymbolAccessible, getObjectFlags, isArrayType, @@ -648,6 +655,7 @@ namespace ts { const unknownType = createIntrinsicType(TypeFlags.Unknown, "unknown"); const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined, "undefined", ObjectFlags.ContainsWideningType); + const optionalType = createIntrinsicType(TypeFlags.Undefined, "undefined"); const nullType = createIntrinsicType(TypeFlags.Null, "null"); const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null, "null", ObjectFlags.ContainsWideningType); const stringType = createIntrinsicType(TypeFlags.String, "string"); @@ -1171,7 +1179,7 @@ namespace ts { // obtain item referenced by 'export=' mainModule = resolveExternalModuleSymbol(mainModule); if (mainModule.flags & SymbolFlags.Namespace) { - // If we’re merging an augmentation to a pattern ambient module, we want to + // If we're merging an augmentation to a pattern ambient module, we want to // perform the merge unidirectionally from the augmentation ('a.foo') to // the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you // all the exports both from the pattern and from the augmentation, but @@ -2657,7 +2665,7 @@ namespace ts { if (patternAmbientModules) { const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference); if (pattern) { - // If the module reference matched a pattern ambient module ('*.foo') but there’s also a + // If the module reference matched a pattern ambient module ('*.foo') but there's also a // module augmentation by the specific name requested ('a.foo'), we store the merged symbol // by the augmentation name ('a.foo'), because asking for *.foo should not give you exports // from a.foo. @@ -8599,6 +8607,12 @@ namespace ts { return result; } + function createOptionalCallSignature(signature: Signature) { + const result = cloneSignature(signature); + result.isOptionalCall = true; + return result; + } + function getExpandedParameters(sig: Signature): readonly Symbol[] { if (sig.hasRestParameter) { const restIndex = sig.parameters.length - 1; @@ -10211,6 +10225,9 @@ namespace ts { signature.unionSignatures ? getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), UnionReduction.Subtype) : getReturnTypeFromAnnotation(signature.declaration!) || (nodeIsMissing((signature.declaration).body) ? anyType : getReturnTypeFromBody(signature.declaration)); + if (signature.isOptionalCall) { + type = propagateOptionalTypeMarker(type, /*wasOptional*/ true); + } if (!popTypeResolution()) { if (signature.declaration) { const typeNode = getEffectiveReturnTypeNode(signature.declaration); @@ -15653,7 +15670,7 @@ namespace ts { for (const sourceProp of excludeProperties(getPropertiesOfType(source), excludedProperties)) { if (!getPropertyOfObjectType(target, sourceProp.escapedName)) { const sourceType = getTypeOfSymbol(sourceProp); - if (!(sourceType === undefinedType || sourceType === undefinedWideningType)) { + if (!(sourceType === undefinedType || sourceType === undefinedWideningType || sourceType === optionalType)) { if (reportErrors) { reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(sourceProp), typeToString(target)); } @@ -16620,6 +16637,53 @@ namespace ts { return strictNullChecks ? getGlobalNonNullableTypeInstantiation(type) : type; } + function addOptionalTypeMarker(type: Type) { + return strictNullChecks ? getUnionType([type, optionalType]) : type; + } + + function removeOptionalTypeMarker(type: Type): Type { + return strictNullChecks ? filterType(type, t => t !== optionalType) : type; + } + + function propagateOptionalTypeMarker(type: Type, wasOptional: boolean) { + return wasOptional ? addOptionalTypeMarker(type) : type; + } + + function createPooledOptionalTypeResult(isOptional: boolean, type: Type) { + pooledOptionalTypeResult.isOptional = isOptional; + pooledOptionalTypeResult.type = type; + return pooledOptionalTypeResult; + } + + function checkOptionalExpression( + parent: PropertyAccessExpression | QualifiedName | ElementAccessExpression | CallExpression, + expression: Expression | QualifiedName, + nullDiagnostic?: DiagnosticMessage, + undefinedDiagnostic?: DiagnosticMessage, + nullOrUndefinedDiagnostic?: DiagnosticMessage, + ) { + let isOptional = false; + let type = checkExpression(expression); + if (isOptionalChain(parent)) { + if (parent.questionDotToken) { + // If we have a questionDotToken then we are an OptionalExpression and should remove `null` and + // `undefined` from the type and add the optionalType to the result, if needed. + isOptional = isNullableType(type); + return createPooledOptionalTypeResult(isOptional, isOptional ? getNonNullableType(type) : type); + } + + // If we do not have a questionDotToken, then we are an OptionalChain and we remove the optionalType and + // indicate whether we need to add optionalType back into the result. + const nonOptionalType = removeOptionalTypeMarker(type); + if (nonOptionalType !== type) { + isOptional = true; + type = nonOptionalType; + } + } + + type = checkNonNullType(type, expression, nullDiagnostic, undefinedDiagnostic, nullOrUndefinedDiagnostic); + return createPooledOptionalTypeResult(isOptional, type); + } /** * Is source potentially coercible to target type under `==`. @@ -18105,7 +18169,7 @@ namespace ts { } function getFlowNodeId(flow: FlowNode): number { - if (!flow.id) { + if (!flow.id || flow.id < 0) { flow.id = nextFlowId; nextFlowId++; } @@ -18653,7 +18717,7 @@ namespace ts { // circularities in control flow analysis, we use getTypeOfDottedName when resolving the call // target expression of an assertion. const funcType = node.parent.kind === SyntaxKind.ExpressionStatement ? getTypeOfDottedName(node.expression, /*diagnostic*/ undefined) : - node.expression.kind !== SyntaxKind.SuperKeyword ? checkNonNullExpression(node.expression) : + node.expression.kind !== SyntaxKind.SuperKeyword ? checkOptionalExpression(node, node.expression).type : undefined; const signatures = getSignaturesOfType(funcType && getApparentType(funcType) || unknownType, SignatureKind.Call); const candidate = signatures.length === 1 && !signatures[0].typeParameters ? signatures[0] : @@ -19571,7 +19635,7 @@ namespace ts { function narrowTypeByCallExpression(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { if (hasMatchingArgument(callExpression, reference)) { - const signature = getEffectsSignature(callExpression); + const signature = assumeTrue || !isCallChain(callExpression) ? getEffectsSignature(callExpression) : undefined; const predicate = signature && getTypePredicateOfSignature(signature); if (predicate && (predicate.kind === TypePredicateKind.This || predicate.kind === TypePredicateKind.Identifier)) { return narrowTypeByTypePredicate(type, predicate, callExpression, assumeTrue); @@ -19614,6 +19678,10 @@ namespace ts { // Narrow the given type based on the given expression having the assumed boolean value. The returned type // will be a subtype or the same type as the argument. function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type { + // for `a?.b`, we emulate a synthetic `a !== null && a !== undefined` condition for `a` + if (isOptionalChainRoot(expr.parent)) { + return narrowTypeByOptionality(type, expr, assumeTrue); + } switch (expr.kind) { case SyntaxKind.Identifier: case SyntaxKind.ThisKeyword: @@ -19635,6 +19703,19 @@ namespace ts { } return type; } + + function narrowTypeByOptionality(type: Type, expr: Expression, assumePresent: boolean): Type { + if (isMatchingReference(reference, expr)) { + return getTypeWithFacts(type, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull); + } + if (isMatchingReferenceDiscriminant(expr, declaredType)) { + return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull)); + } + if (containsMatchingReferenceDiscriminant(reference, expr)) { + return declaredType; + } + return type; + } } function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) { @@ -22505,12 +22586,12 @@ namespace ts { ); } + function isNullableType(type: Type) { + return !!((strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable); + } + function getNonNullableTypeIfNeeded(type: Type) { - const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable; - if (kind) { - return getNonNullableType(type); - } - return type; + return isNullableType(type) ? getNonNullableType(type) : type; } function checkNonNullType( @@ -22561,8 +22642,7 @@ namespace ts { } function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) { - let propType: Type; - const leftType = checkNonNullExpression(left); + const { isOptional, type: leftType } = checkOptionalExpression(node, left); const parentSymbol = getNodeLinks(left).resolvedSymbol; const assignmentKind = getAssignmentTargetKind(node); const apparentType = getApparentType(assignmentKind !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(leftType) : leftType); @@ -22576,6 +22656,8 @@ namespace ts { if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) { markAliasReferenced(parentSymbol, node); } + + let propType: Type; if (!prop) { const indexInfo = assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined; if (!(indexInfo && indexInfo.type)) { @@ -22614,7 +22696,7 @@ namespace ts { } propType = getConstraintForLocation(getTypeOfSymbol(prop), node); } - return getFlowTypeOfAccessExpression(node, prop, propType, right); + return propagateOptionalTypeMarker(getFlowTypeOfAccessExpression(node, prop, propType, right), isOptional); } function getFlowTypeOfAccessExpression(node: ElementAccessExpression | PropertyAccessExpression | QualifiedName, prop: Symbol | undefined, propType: Type, errorNode: Node) { @@ -22971,9 +23053,8 @@ namespace ts { } function checkIndexedAccess(node: ElementAccessExpression): Type { - const exprType = checkNonNullExpression(node.expression); + const { isOptional, type: exprType } = checkOptionalExpression(node, node.expression); const objectType = getAssignmentTargetKind(node) !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(exprType) : exprType; - const indexExpression = node.argumentExpression; const indexType = checkExpression(indexExpression); @@ -22991,7 +23072,7 @@ namespace ts { AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) : AccessFlags.None; const indexedAccessType = getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, node, accessFlags) || errorType; - return checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node); + return propagateOptionalTypeMarker(checkIndexedAccessIndexType(getFlowTypeOfAccessExpression(node, indexedAccessType.symbol, indexedAccessType, indexExpression), node), isOptional); } function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean { @@ -23074,7 +23155,7 @@ namespace ts { // interface B extends A { (x: 'foo'): string } // const b: B; // b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void] - function reorderCandidates(signatures: readonly Signature[], result: Signature[]): void { + function reorderCandidates(signatures: readonly Signature[], result: Signature[], isOptionalCall: boolean): void { let lastParent: Node | undefined; let lastSymbol: Symbol | undefined; let cutoffIndex = 0; @@ -23116,7 +23197,7 @@ namespace ts { spliceIndex = index; } - result.splice(spliceIndex, 0, signature); + result.splice(spliceIndex, 0, isOptionalCall ? createOptionalCallSignature(signature) : signature); } } @@ -23769,7 +23850,7 @@ namespace ts { return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount); } - function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, fallbackError?: DiagnosticMessage): Signature { + function resolveCall(node: CallLikeExpression, signatures: readonly Signature[], candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, isOptionalCall: boolean, fallbackError?: DiagnosticMessage): Signature { const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression; const isDecorator = node.kind === SyntaxKind.Decorator; const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node); @@ -23788,7 +23869,7 @@ namespace ts { const candidates = candidatesOutArray || []; // reorderCandidates fills up the candidates array directly - reorderCandidates(signatures, candidates); + reorderCandidates(signatures, candidates, isOptionalCall); if (!candidates.length) { if (reportErrors) { diagnostics.add(getDiagnosticForCallNode(node, Diagnostics.Call_target_does_not_contain_any_signatures)); @@ -24172,13 +24253,14 @@ namespace ts { const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!); if (baseTypeNode) { const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode); - return resolveCall(node, baseConstructors, candidatesOutArray, checkMode); + return resolveCall(node, baseConstructors, candidatesOutArray, checkMode, /*isOptional*/ false); } } return resolveUntypedCall(node); } - const funcType = checkNonNullExpression( + const { isOptional, type: funcType } = checkOptionalExpression( + node, node.expression, Diagnostics.Cannot_invoke_an_object_which_is_possibly_null, Diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined, @@ -24188,8 +24270,8 @@ namespace ts { if (funcType === silentNeverType) { return silentNeverSignature; } - const apparentType = getApparentType(funcType); + const apparentType = getApparentType(funcType); if (apparentType === errorType) { // Another error has already been reported return resolveErrorCall(node); @@ -24253,7 +24335,8 @@ namespace ts { error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType)); return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray, checkMode); + + return resolveCall(node, callSignatures, candidatesOutArray, checkMode, isOptional); } function isGenericFunctionReturningFunction(signature: Signature) { @@ -24324,7 +24407,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, constructSignatures, candidatesOutArray, checkMode); + return resolveCall(node, constructSignatures, candidatesOutArray, checkMode, /*isOptional*/ false); } // If expressionType's apparent type is an object type with no construct signatures but @@ -24333,7 +24416,7 @@ namespace ts { // operation is Any. It is an error to have a Void this type. const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call); if (callSignatures.length) { - const signature = resolveCall(node, callSignatures, candidatesOutArray, checkMode); + const signature = resolveCall(node, callSignatures, candidatesOutArray, checkMode, /*isOptional*/ false); if (!noImplicitAny) { if (signature.declaration && !isJSConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) { error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); @@ -24548,7 +24631,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray, checkMode); + return resolveCall(node, callSignatures, candidatesOutArray, checkMode, /*isOptional*/ false); } /** @@ -24611,7 +24694,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, callSignatures, candidatesOutArray, checkMode, headMessage); + return resolveCall(node, callSignatures, candidatesOutArray, checkMode, /*isOptional*/ false, headMessage); } function createSignatureForJSXIntrinsic(node: JsxOpeningLikeElement, result: Type): Signature { @@ -24664,7 +24747,7 @@ namespace ts { return resolveErrorCall(node); } - return resolveCall(node, signatures, candidatesOutArray, checkMode); + return resolveCall(node, signatures, candidatesOutArray, checkMode, /*isOptional*/ false); } /** @@ -24851,18 +24934,20 @@ namespace ts { getTypeOfDottedName(node.expression, diagnostic); } } - let jsAssignmentType: Type | undefined; + if (isInJSFile(node)) { const decl = getDeclarationOfExpando(node); if (decl) { const jsSymbol = getSymbolOfNode(decl); if (jsSymbol && hasEntries(jsSymbol.exports)) { - jsAssignmentType = createAnonymousType(jsSymbol, jsSymbol.exports, emptyArray, emptyArray, undefined, undefined); - (jsAssignmentType as ObjectType).objectFlags |= ObjectFlags.JSLiteral; + const jsAssignmentType = createAnonymousType(jsSymbol, jsSymbol.exports, emptyArray, emptyArray, undefined, undefined); + jsAssignmentType.objectFlags |= ObjectFlags.JSLiteral; + return getIntersectionType([returnType, jsAssignmentType]); } } } - return jsAssignmentType ? getIntersectionType([returnType, jsAssignmentType]) : returnType; + + return returnType; } function isSymbolOrSymbolForCall(node: Node) { @@ -24969,7 +25054,7 @@ namespace ts { } function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type { - checkGrammarTypeArguments(node, node.typeArguments); + if (!checkGrammarTaggedTemplateChain(node)) checkGrammarTypeArguments(node, node.typeArguments); if (languageVersion < ScriptTarget.ES2015) { checkExternalEmitHelpers(node, ExternalEmitHelpers.MakeTemplateObject); } @@ -25886,13 +25971,17 @@ namespace ts { return false; } - function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage): boolean { + function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, invalidOptionalChainMessage: DiagnosticMessage): boolean { // References are combinations of identifiers, parentheses, and property accesses. const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses); if (node.kind !== SyntaxKind.Identifier && node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) { error(expr, invalidReferenceMessage); return false; } + if (node.flags & NodeFlags.OptionalChain) { + error(expr, invalidOptionalChainMessage); + return false; + } return true; } @@ -26002,7 +26091,10 @@ namespace ts { Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type); if (ok) { // run check only if former checks succeeded to avoid reporting cascading errors - checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access); + checkReferenceExpression( + node.operand, + Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, + Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access); } return getUnaryResultType(operandType); } @@ -26020,7 +26112,10 @@ namespace ts { Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type); if (ok) { // run check only if former checks succeeded to avoid reporting cascading errors - checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access); + checkReferenceExpression( + node.operand, + Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, + Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access); } return getUnaryResultType(operandType); } @@ -26270,7 +26365,10 @@ namespace ts { const error = target.parent.kind === SyntaxKind.SpreadAssignment ? Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access : Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access; - if (checkReferenceExpression(target, error)) { + const optionalError = target.parent.kind === SyntaxKind.SpreadAssignment ? + Diagnostics.The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access : + Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access; + if (checkReferenceExpression(target, error, optionalError)) { checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target); } return sourceType; @@ -26476,7 +26574,7 @@ namespace ts { if (!resultType) { // Types that have a reasonably good chance of being a valid operand type. - // If both types have an awaited type of one of these, we’ll assume the user + // If both types have an awaited type of one of these, we'll assume the user // might be missing an await without doing an exhaustive check that inserting // await(s) will actually be a completely valid binary expression. const closeEnoughKind = TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.AnyOrUnknown; @@ -26614,7 +26712,9 @@ namespace ts { // A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1) // and the type of the non-compound operation to be assignable to the type of VarExpr. - if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access) + if (checkReferenceExpression(left, + Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access, + Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access) && (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) { // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right); @@ -27113,11 +27213,11 @@ namespace ts { const expr = skipParentheses(node); // Optimize for the common case of a call to a function with a single non-generic call // signature where we can just fetch the return type without checking the arguments. - if (expr.kind === SyntaxKind.CallExpression && (expr).expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) { - const funcType = checkNonNullExpression((expr).expression); + if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) { + const { isOptional, type: funcType } = checkOptionalExpression(expr, expr.expression); const signature = getSingleCallSignature(funcType); if (signature && !signature.typeParameters) { - return getReturnTypeOfSignature(signature); + return propagateOptionalTypeMarker(getReturnTypeOfSignature(signature), isOptional); } } else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) { @@ -29958,7 +30058,10 @@ namespace ts { } else { const leftType = checkExpression(varExpr); - checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access); + checkReferenceExpression( + varExpr, + Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access, + Diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_an_optional_property_access); // iteratedType will be undefined if the rightType was missing properties/signatures // required to get its iteratedType (like [Symbol.iterator] or next). This may be @@ -30008,7 +30111,10 @@ namespace ts { } else { // run check only former check succeeded to avoid cascading errors - checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access); + checkReferenceExpression( + varExpr, + Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access, + Diagnostics.The_left_hand_side_of_a_for_in_statement_may_not_be_an_optional_property_access); } } @@ -34799,6 +34905,13 @@ namespace ts { checkGrammarForAtLeastOneTypeArgument(node, typeArguments); } + function checkGrammarTaggedTemplateChain(node: TaggedTemplateExpression): boolean { + if (node.questionDotToken || node.flags & NodeFlags.OptionalChain) { + return grammarErrorOnNode(node.template, Diagnostics.Tagged_template_expressions_are_not_permitted_in_an_optional_chain); + } + return false; + } + function checkGrammarForOmittedArgument(args: NodeArray | undefined): boolean { if (args) { for (const arg of args) { diff --git a/src/compiler/debug.ts b/src/compiler/debug.ts index 7e61653578415..09d230f24e9e8 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -187,6 +187,14 @@ namespace ts { assertNode) : noop; + export const assertNotNode = shouldAssert(AssertionLevel.Normal) + ? (node: Node | undefined, test: ((node: Node | undefined) => boolean) | undefined, message?: string): void => assert( + test === undefined || !test(node), + message || "Unexpected node.", + () => `Node ${formatSyntaxKind(node!.kind)} should not have passed test '${getFunctionName(test!)}'.`, + assertNode) + : noop; + export const assertOptionalNode = shouldAssert(AssertionLevel.Normal) ? (node: Node, test: (node: Node) => boolean, message?: string): void => assert( test === undefined || node === undefined || test(node), @@ -260,5 +268,6 @@ namespace ts { isDebugInfoEnabled = true; } + } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8754e8deac694..67be36b706e92 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1039,6 +1039,10 @@ "category": "Error", "code": 1357 }, + "Tagged template expressions are not permitted in an optional chain.": { + "category": "Error", + "code": 1358 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", @@ -2747,6 +2751,27 @@ "category": "Error", "code": 2776 }, + "The operand of an increment or decrement operator may not be an optional property access.": { + "category": "Error", + "code": 2777 + }, + "The target of an object rest assignment may not be an optional property access.": { + "category": "Error", + "code": 2778 + }, + "The left-hand side of an assignment expression may not be an optional property access.": { + "category": "Error", + "code": 2779 + }, + "The left-hand side of a 'for...in' statement may not be an optional property access.": { + "category": "Error", + "code": 2780 + }, + "The left-hand side of a 'for...of' statement may not be an optional property access.": { + "category": "Error", + "code": 2781 + }, + "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 36a70ec7d2562..fc6f7002b42ae 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -858,6 +858,8 @@ namespace ts { let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[] | undefined; let hasWrittenComment = false; let commentsDisabled = !!printerOptions.removeComments; + let lastNode: Node | undefined; + let lastSubstitution: Node | undefined; const { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); reset(); @@ -1080,8 +1082,7 @@ namespace ts { setSourceFile(sourceFile); } - const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node); - pipelinePhase(hint, node); + pipelineEmit(hint, node); } function setSourceFile(sourceFile: SourceFile | undefined) { @@ -1113,6 +1114,8 @@ namespace ts { currentSourceFile = undefined!; currentLineMap = undefined!; detachedCommentsInfo = undefined; + lastNode = undefined; + lastSubstitution = undefined; setWriter(/*output*/ undefined, /*_sourceMapGenerator*/ undefined); } @@ -1120,24 +1123,47 @@ namespace ts { return currentLineMap || (currentLineMap = getLineStarts(currentSourceFile!)); } + function emit(node: Node): Node; + function emit(node: Node | undefined): Node | undefined; function emit(node: Node | undefined) { if (node === undefined) return; + const prevSourceFileTextKind = recordBundleFileInternalSectionStart(node); - const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node); - pipelinePhase(EmitHint.Unspecified, node); + const substitute = pipelineEmit(EmitHint.Unspecified, node); recordBundleFileInternalSectionEnd(prevSourceFileTextKind); + return substitute; } - function emitIdentifierName(node: Identifier | undefined) { + function emitIdentifierName(node: Identifier): Node; + function emitIdentifierName(node: Identifier | undefined): Node | undefined; + function emitIdentifierName(node: Identifier | undefined): Node | undefined { if (node === undefined) return; - const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node); - pipelinePhase(EmitHint.IdentifierName, node); + return pipelineEmit(EmitHint.IdentifierName, node); } - function emitExpression(node: Expression | undefined) { + function emitExpression(node: Expression): Node; + function emitExpression(node: Expression | undefined): Node | undefined; + function emitExpression(node: Expression | undefined): Node | undefined { if (node === undefined) return; + return pipelineEmit(EmitHint.Expression, node); + } + + function pipelineEmit(emitHint: EmitHint, node: Node) { + const savedLastNode = lastNode; + const savedLastSubstitution = lastSubstitution; + lastNode = node; + lastSubstitution = undefined; + const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node); - pipelinePhase(EmitHint.Expression, node); + pipelinePhase(emitHint, node); + + Debug.assert(lastNode === node); + + const substitute = lastSubstitution; + lastNode = savedLastNode; + lastSubstitution = savedLastSubstitution; + + return substitute || node; } function getPipelinePhase(phase: PipelinePhase, node: Node) { @@ -1179,11 +1205,14 @@ namespace ts { } function pipelineEmitWithNotification(hint: EmitHint, node: Node) { + Debug.assert(lastNode === node); const pipelinePhase = getNextPipelinePhase(PipelinePhase.Notification, node); onEmitNode(hint, node, pipelinePhase); + Debug.assert(lastNode === node); } function pipelineEmitWithHint(hint: EmitHint, node: Node): void { + Debug.assert(lastNode === node || lastSubstitution === node); if (hint === EmitHint.SourceFile) return emitSourceFile(cast(node, isSourceFile)); if (hint === EmitHint.IdentifierName) return emitIdentifier(cast(node, isIdentifier)); if (hint === EmitHint.MappedTypeParameter) return emitMappedTypeParameter(cast(node, isTypeParameterDeclaration)); @@ -1495,7 +1524,7 @@ namespace ts { if (isExpression(node)) { hint = EmitHint.Expression; if (substituteNode !== noEmitSubstitution) { - node = substituteNode(hint, node); + lastSubstitution = node = substituteNode(hint, node); } } else if (isToken(node)) { @@ -1611,8 +1640,11 @@ namespace ts { } function pipelineEmitWithSubstitution(hint: EmitHint, node: Node) { + Debug.assert(lastNode === node || lastSubstitution === node); const pipelinePhase = getNextPipelinePhase(PipelinePhase.Substitution, node); - pipelinePhase(hint, substituteNode(hint, node)); + lastSubstitution = substituteNode(hint, node); + pipelinePhase(hint, lastSubstitution); + Debug.assert(lastNode === node || lastSubstitution === node); } function getHelpersFromBundledSourceFiles(bundle: Bundle): string[] | undefined { @@ -2116,8 +2148,7 @@ namespace ts { } writePunctuation("["); - const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node.typeParameter); - pipelinePhase(EmitHint.MappedTypeParameter, node.typeParameter); + pipelineEmit(EmitHint.MappedTypeParameter, node.typeParameter); writePunctuation("]"); if (node.questionToken) { @@ -2215,34 +2246,24 @@ namespace ts { } function emitPropertyAccessExpression(node: PropertyAccessExpression) { - let indentBeforeDot = false; - let indentAfterDot = false; - const dotRangeFirstCommentStart = skipTrivia( - currentSourceFile!.text, - node.expression.end, - /*stopAfterLineBreak*/ false, - /*stopAtComments*/ true - ); - const dotRangeStart = skipTrivia(currentSourceFile!.text, dotRangeFirstCommentStart); - const dotRangeEnd = dotRangeStart + 1; - if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { - const dotToken = createToken(SyntaxKind.DotToken); - dotToken.pos = node.expression.end; - dotToken.end = dotRangeEnd; - indentBeforeDot = needsIndentation(node, node.expression, dotToken); - indentAfterDot = needsIndentation(node, dotToken, node.name); - } + const expression = cast(emitExpression(node.expression), isExpression); + const token = getDotOrQuestionDotToken(node); + const indentBeforeDot = needsIndentation(node, node.expression, token); + const indentAfterDot = needsIndentation(node, token, node.name); - emitExpression(node.expression); increaseIndentIf(indentBeforeDot, /*writeSpaceIfNotIndenting*/ false); - const dotHasCommentTrivia = dotRangeFirstCommentStart !== dotRangeStart; - const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression, dotHasCommentTrivia); + const shouldEmitDotDot = + token.kind !== SyntaxKind.QuestionDotToken && + mayNeedDotDotForPropertyAccess(expression) && + !writer.hasTrailingComment() && + !writer.hasTrailingWhitespace(); + if (shouldEmitDotDot) { writePunctuation("."); } - emitTokenWithComment(SyntaxKind.DotToken, node.expression.end, writePunctuation, node); + emitTokenWithComment(token.kind, node.expression.end, writePunctuation, node); increaseIndentIf(indentAfterDot, /*writeSpaceIfNotIndenting*/ false); emit(node.name); decreaseIndentIf(indentBeforeDot, indentAfterDot); @@ -2250,28 +2271,27 @@ namespace ts { // 1..toString is a valid property access, emit a dot after the literal // Also emit a dot if expression is a integer const enum value - it will appear in generated code as numeric literal - function needsDotDotForPropertyAccess(expression: Expression, dotHasTrivia: boolean) { + function mayNeedDotDotForPropertyAccess(expression: Expression) { expression = skipPartiallyEmittedExpressions(expression); if (isNumericLiteral(expression)) { // check if numeric literal is a decimal literal that was originally written with a dot const text = getLiteralTextOfNode(expression, /*neverAsciiEscape*/ true); // If he number will be printed verbatim and it doesn't already contain a dot, add one // if the expression doesn't have any comments that will be emitted. - return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!) && - (!dotHasTrivia || printerOptions.removeComments); + return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!); } else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) { // check if constant enum value is integer const constantValue = getConstantValue(expression); // isFinite handles cases when constantValue is undefined return typeof constantValue === "number" && isFinite(constantValue) - && Math.floor(constantValue) === constantValue - && printerOptions.removeComments; + && Math.floor(constantValue) === constantValue; } } function emitElementAccessExpression(node: ElementAccessExpression) { emitExpression(node.expression); + emit(node.questionDotToken); emitTokenWithComment(SyntaxKind.OpenBracketToken, node.expression.end, writePunctuation, node); emitExpression(node.argumentExpression); emitTokenWithComment(SyntaxKind.CloseBracketToken, node.argumentExpression.end, writePunctuation, node); @@ -2279,6 +2299,7 @@ namespace ts { function emitCallExpression(node: CallExpression) { emitExpression(node.expression); + emit(node.questionDotToken); emitTypeArguments(node, node.typeArguments); emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); } @@ -3742,8 +3763,7 @@ namespace ts { writeLine(); increaseIndent(); if (isEmptyStatement(node)) { - const pipelinePhase = getPipelinePhase(PipelinePhase.Notification, node); - pipelinePhase(EmitHint.EmbeddedStatement, node); + pipelineEmit(EmitHint.EmbeddedStatement, node); } else { emit(node); @@ -4214,6 +4234,10 @@ namespace ts { } function needsIndentation(parent: Node, node1: Node, node2: Node): boolean { + if (getEmitFlags(parent) & EmitFlags.NoIndentation) { + return false; + } + parent = skipSynthesizedParentheses(parent); node1 = skipSynthesizedParentheses(node1); node2 = skipSynthesizedParentheses(node2); @@ -4669,6 +4693,7 @@ namespace ts { // Comments function pipelineEmitWithComments(hint: EmitHint, node: Node) { + Debug.assert(lastNode === node || lastSubstitution === node); enterComment(); hasWrittenComment = false; const emitFlags = getEmitFlags(node); @@ -4735,6 +4760,7 @@ namespace ts { } } exitComment(); + Debug.assert(lastNode === node || lastSubstitution === node); } function emitLeadingSynthesizedComment(comment: SynthesizedComment) { @@ -4981,6 +5007,7 @@ namespace ts { } function pipelineEmitWithSourceMap(hint: EmitHint, node: Node) { + Debug.assert(lastNode === node || lastSubstitution === node); const pipelinePhase = getNextPipelinePhase(PipelinePhase.SourceMaps, node); if (isUnparsedSource(node) || isUnparsedPrepend(node)) { pipelinePhase(hint, node); @@ -5023,6 +5050,7 @@ namespace ts { emitSourcePos(source, end); } } + Debug.assert(lastNode === node || lastSubstitution === node); } /** diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 95220fec0be62..7d32cc884fc43 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1065,6 +1065,7 @@ namespace ts { } export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier) { + Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update a PropertyAccessChain using updatePropertyAccess. Use updatePropertyAccessChain instead."); // Because we are updating existed propertyAccess we want to inherit its emitFlags // instead of using the default from createPropertyAccess return node.expression !== expression @@ -1073,6 +1074,27 @@ namespace ts { : node; } + export function createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier) { + const node = createSynthesizedNode(SyntaxKind.PropertyAccessExpression); + node.flags |= NodeFlags.OptionalChain; + node.expression = parenthesizeForAccess(expression); + node.questionDotToken = questionDotToken; + node.name = asName(name); + setEmitFlags(node, EmitFlags.NoIndentation); + return node; + } + + export function updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier) { + Debug.assert(!!(node.flags & NodeFlags.OptionalChain), "Cannot update a PropertyAccessExpression using updatePropertyAccessChain. Use updatePropertyAccess instead."); + // Because we are updating an existing PropertyAccessChain we want to inherit its emitFlags + // instead of using the default from createPropertyAccess + return node.expression !== expression + || node.questionDotToken !== questionDotToken + || node.name !== name + ? updateNode(setEmitFlags(createPropertyAccessChain(expression, questionDotToken, name), getEmitFlags(node)), node) + : node; + } + export function createElementAccess(expression: Expression, index: number | Expression) { const node = createSynthesizedNode(SyntaxKind.ElementAccessExpression); node.expression = parenthesizeForAccess(expression); @@ -1081,12 +1103,31 @@ namespace ts { } export function updateElementAccess(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression) { + Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update an ElementAccessChain using updateElementAccess. Use updateElementAccessChain instead."); return node.expression !== expression || node.argumentExpression !== argumentExpression ? updateNode(createElementAccess(expression, argumentExpression), node) : node; } + export function createElementAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, index: number | Expression) { + const node = createSynthesizedNode(SyntaxKind.ElementAccessExpression); + node.flags |= NodeFlags.OptionalChain; + node.expression = parenthesizeForAccess(expression); + node.questionDotToken = questionDotToken; + node.argumentExpression = asExpression(index); + return node; + } + + export function updateElementAccessChain(node: ElementAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, argumentExpression: Expression) { + Debug.assert(!!(node.flags & NodeFlags.OptionalChain), "Cannot update an ElementAccessExpression using updateElementAccessChain. Use updateElementAccess instead."); + return node.expression !== expression + || node.questionDotToken !== questionDotToken + || node.argumentExpression !== argumentExpression + ? updateNode(createElementAccessChain(expression, questionDotToken, argumentExpression), node) + : node; + } + export function createCall(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined) { const node = createSynthesizedNode(SyntaxKind.CallExpression); node.expression = parenthesizeForAccess(expression); @@ -1096,6 +1137,7 @@ namespace ts { } export function updateCall(node: CallExpression, expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]) { + Debug.assert(!(node.flags & NodeFlags.OptionalChain), "Cannot update a CallChain using updateCall. Use updateCallChain instead."); return node.expression !== expression || node.typeArguments !== typeArguments || node.arguments !== argumentsArray @@ -1103,6 +1145,26 @@ namespace ts { : node; } + export function createCallChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined) { + const node = createSynthesizedNode(SyntaxKind.CallExpression); + node.flags |= NodeFlags.OptionalChain; + node.expression = parenthesizeForAccess(expression); + node.questionDotToken = questionDotToken; + node.typeArguments = asNodeArray(typeArguments); + node.arguments = parenthesizeListElements(createNodeArray(argumentsArray)); + return node; + } + + export function updateCallChain(node: CallChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]) { + Debug.assert(!!(node.flags & NodeFlags.OptionalChain), "Cannot update a CallExpression using updateCallChain. Use updateCall instead."); + return node.expression !== expression + || node.questionDotToken !== questionDotToken + || node.typeArguments !== typeArguments + || node.arguments !== argumentsArray + ? updateNode(createCallChain(expression, questionDotToken, typeArguments, argumentsArray), node) + : node; + } + export function createNew(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined) { const node = createSynthesizedNode(SyntaxKind.NewExpression); node.expression = parenthesizeForNew(expression); @@ -2776,6 +2838,22 @@ namespace ts { : node; } + /* @internal */ + export function createSyntheticReferenceExpression(expression: Expression, thisArg: Expression) { + const node = createSynthesizedNode(SyntaxKind.SyntheticReferenceExpression); + node.expression = expression; + node.thisArg = thisArg; + return node; + } + + /* @internal */ + export function updateSyntheticReferenceExpression(node: SyntheticReferenceExpression, expression: Expression, thisArg: Expression) { + return node.expression !== expression + || node.thisArg !== thisArg + ? updateNode(createSyntheticReferenceExpression(expression, thisArg), node) + : node; + } + export function createBundle(sourceFiles: readonly SourceFile[], prepends: readonly (UnparsedSource | InputFiles)[] = emptyArray) { const node = createNode(SyntaxKind.Bundle); node.prepends = prepends; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index bad247b175dca..7a02e412fddae 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -212,17 +212,21 @@ namespace ts { return visitNodes(cbNode, cbNodes, (node).properties); case SyntaxKind.PropertyAccessExpression: return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).questionDotToken) || visitNode(cbNode, (node).name); case SyntaxKind.ElementAccessExpression: return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).questionDotToken) || visitNode(cbNode, (node).argumentExpression); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).questionDotToken) || visitNodes(cbNode, cbNodes, (node).typeArguments) || visitNodes(cbNode, cbNodes, (node).arguments); case SyntaxKind.TaggedTemplateExpression: return visitNode(cbNode, (node).tag) || + visitNode(cbNode, (node).questionDotToken) || visitNodes(cbNode, cbNodes, (node).typeArguments) || visitNode(cbNode, (node).template); case SyntaxKind.TypeAssertionExpression: @@ -4243,7 +4247,8 @@ namespace ts { } // Now, we *may* be complete. However, we might have consumed the start of a - // CallExpression. As such, we need to consume the rest of it here to be complete. + // CallExpression or OptionalExpression. As such, we need to consume the rest + // of it here to be complete. return parseCallExpressionRest(expression); } @@ -4296,7 +4301,7 @@ namespace ts { // Because CallExpression and MemberExpression are left recursive, we need to bottom out // of the recursion immediately. So we parse out a primary expression to start with. const expression = parsePrimaryExpression(); - return parseMemberExpressionRest(expression); + return parseMemberExpressionRest(expression, /*allowOptionalChain*/ true); } function parseSuperExpression(): MemberExpression { @@ -4590,18 +4595,70 @@ namespace ts { return finishNode(node); } - function parseMemberExpressionRest(expression: LeftHandSideExpression): MemberExpression { + function nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate() { + nextToken(); + return tokenIsIdentifierOrKeyword(token()) + || token() === SyntaxKind.OpenBracketToken + || isTemplateStartOfTaggedTemplate(); + } + + function isStartOfOptionalPropertyOrElementAccessChain() { + return token() === SyntaxKind.QuestionDotToken + && lookAhead(nextTokenIsIdentifierOrKeywordOrOpenBracketOrTemplate); + } + + function parsePropertyAccessExpressionRest(expression: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined) { + const propertyAccess = createNode(SyntaxKind.PropertyAccessExpression, expression.pos); + propertyAccess.expression = expression; + propertyAccess.questionDotToken = questionDotToken; + propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true); + if (questionDotToken || expression.flags & NodeFlags.OptionalChain) { + propertyAccess.flags |= NodeFlags.OptionalChain; + } + return finishNode(propertyAccess); + } + + function parseElementAccessExpressionRest(expression: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined) { + const indexedAccess = createNode(SyntaxKind.ElementAccessExpression, expression.pos); + indexedAccess.expression = expression; + indexedAccess.questionDotToken = questionDotToken; + + if (token() === SyntaxKind.CloseBracketToken) { + indexedAccess.argumentExpression = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.An_element_access_expression_should_take_an_argument); + } + else { + const argument = allowInAnd(parseExpression); + if (isStringOrNumericLiteralLike(argument)) { + argument.text = internIdentifier(argument.text); + } + indexedAccess.argumentExpression = argument; + } + + parseExpected(SyntaxKind.CloseBracketToken); + if (questionDotToken || expression.flags & NodeFlags.OptionalChain) { + indexedAccess.flags |= NodeFlags.OptionalChain; + } + return finishNode(indexedAccess); + } + + function parseMemberExpressionRest(expression: LeftHandSideExpression, allowOptionalChain: boolean): MemberExpression { while (true) { - const dotToken = parseOptionalToken(SyntaxKind.DotToken); - if (dotToken) { - const propertyAccess = createNode(SyntaxKind.PropertyAccessExpression, expression.pos); - propertyAccess.expression = expression; - propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true); - expression = finishNode(propertyAccess); + let questionDotToken: QuestionDotToken | undefined; + let isPropertyAccess = false; + if (allowOptionalChain && isStartOfOptionalPropertyOrElementAccessChain()) { + questionDotToken = parseExpectedToken(SyntaxKind.QuestionDotToken); + isPropertyAccess = tokenIsIdentifierOrKeyword(token()); + } + else { + isPropertyAccess = parseOptional(SyntaxKind.DotToken); + } + + if (isPropertyAccess) { + expression = parsePropertyAccessExpressionRest(expression, questionDotToken); continue; } - if (token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) { + if (!questionDotToken && token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) { nextToken(); const nonNullExpression = createNode(SyntaxKind.NonNullExpression, expression.pos); nonNullExpression.expression = expression; @@ -4610,28 +4667,13 @@ namespace ts { } // when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName - if (!inDecoratorContext() && parseOptional(SyntaxKind.OpenBracketToken)) { - const indexedAccess = createNode(SyntaxKind.ElementAccessExpression, expression.pos); - indexedAccess.expression = expression; - - if (token() === SyntaxKind.CloseBracketToken) { - indexedAccess.argumentExpression = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.An_element_access_expression_should_take_an_argument); - } - else { - const argument = allowInAnd(parseExpression); - if (isStringOrNumericLiteralLike(argument)) { - argument.text = internIdentifier(argument.text); - } - indexedAccess.argumentExpression = argument; - } - - parseExpected(SyntaxKind.CloseBracketToken); - expression = finishNode(indexedAccess); + if ((questionDotToken || !inDecoratorContext()) && parseOptional(SyntaxKind.OpenBracketToken)) { + expression = parseElementAccessExpressionRest(expression, questionDotToken); continue; } if (isTemplateStartOfTaggedTemplate()) { - expression = parseTaggedTemplateRest(expression, /*typeArguments*/ undefined); + expression = parseTaggedTemplateRest(expression, questionDotToken, /*typeArguments*/ undefined); continue; } @@ -4643,19 +4685,25 @@ namespace ts { return token() === SyntaxKind.NoSubstitutionTemplateLiteral || token() === SyntaxKind.TemplateHead; } - function parseTaggedTemplateRest(tag: LeftHandSideExpression, typeArguments: NodeArray | undefined) { + function parseTaggedTemplateRest(tag: LeftHandSideExpression, questionDotToken: QuestionDotToken | undefined, typeArguments: NodeArray | undefined) { const tagExpression = createNode(SyntaxKind.TaggedTemplateExpression, tag.pos); tagExpression.tag = tag; + tagExpression.questionDotToken = questionDotToken; tagExpression.typeArguments = typeArguments; tagExpression.template = token() === SyntaxKind.NoSubstitutionTemplateLiteral ? parseLiteralNode() : parseTemplateExpression(); + if (questionDotToken || tag.flags & NodeFlags.OptionalChain) { + tagExpression.flags |= NodeFlags.OptionalChain; + } return finishNode(tagExpression); } function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression { while (true) { - expression = parseMemberExpressionRest(expression); + expression = parseMemberExpressionRest(expression, /*allowOptionalChain*/ true); + const questionDotToken = parseOptionalToken(SyntaxKind.QuestionDotToken); + // handle 'foo<()' if (token() === SyntaxKind.LessThanToken || token() === SyntaxKind.LessThanLessThanToken) { // See if this is the start of a generic invocation. If so, consume it and @@ -4663,32 +4711,47 @@ namespace ts { // part of an arithmetic expression. Break out so we consume it higher in the // stack. const typeArguments = tryParse(parseTypeArgumentsInExpression); - if (!typeArguments) { - return expression; - } + if (typeArguments) { + if (isTemplateStartOfTaggedTemplate()) { + expression = parseTaggedTemplateRest(expression, questionDotToken, typeArguments); + continue; + } - if (isTemplateStartOfTaggedTemplate()) { - expression = parseTaggedTemplateRest(expression, typeArguments); + const callExpr = createNode(SyntaxKind.CallExpression, expression.pos); + callExpr.expression = expression; + callExpr.questionDotToken = questionDotToken; + callExpr.typeArguments = typeArguments; + callExpr.arguments = parseArgumentList(); + if (questionDotToken || expression.flags & NodeFlags.OptionalChain) { + callExpr.flags |= NodeFlags.OptionalChain; + } + expression = finishNode(callExpr); continue; } - - const callExpr = createNode(SyntaxKind.CallExpression, expression.pos); - callExpr.expression = expression; - callExpr.typeArguments = typeArguments; - callExpr.arguments = parseArgumentList(); - expression = finishNode(callExpr); - continue; } else if (token() === SyntaxKind.OpenParenToken) { const callExpr = createNode(SyntaxKind.CallExpression, expression.pos); callExpr.expression = expression; + callExpr.questionDotToken = questionDotToken; callExpr.arguments = parseArgumentList(); + if (questionDotToken || expression.flags & NodeFlags.OptionalChain) { + callExpr.flags |= NodeFlags.OptionalChain; + } expression = finishNode(callExpr); continue; } - - return expression; + if (questionDotToken) { + // We failed to parse anything, so report a missing identifier here. + const propertyAccess = createNode(SyntaxKind.PropertyAccessExpression, expression.pos) as PropertyAccessExpression; + propertyAccess.expression = expression; + propertyAccess.questionDotToken = questionDotToken; + propertyAccess.name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics.Identifier_expected); + propertyAccess.flags |= NodeFlags.OptionalChain; + expression = finishNode(propertyAccess); + } + break; } + return expression; } function parseArgumentList() { @@ -4957,12 +5020,12 @@ namespace ts { let expression: MemberExpression = parsePrimaryExpression(); let typeArguments; while (true) { - expression = parseMemberExpressionRest(expression); + expression = parseMemberExpressionRest(expression, /*allowOptionalChain*/ false); typeArguments = tryParse(parseTypeArgumentsInExpression); if (isTemplateStartOfTaggedTemplate()) { Debug.assert(!!typeArguments, "Expected a type argument list; all plain tagged template starts should be consumed in 'parseMemberExpressionRest'"); - expression = parseTaggedTemplateRest(expression, typeArguments); + expression = parseTaggedTemplateRest(expression, /*optionalChain*/ undefined, typeArguments); typeArguments = undefined; } break; diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 1de4327d2b4df..18c96f94127d2 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -184,6 +184,7 @@ namespace ts { "&&": SyntaxKind.AmpersandAmpersandToken, "||": SyntaxKind.BarBarToken, "?": SyntaxKind.QuestionToken, + "?.": SyntaxKind.QuestionDotToken, ":": SyntaxKind.ColonToken, "=": SyntaxKind.EqualsToken, "+=": SyntaxKind.PlusEqualsToken, @@ -1829,6 +1830,10 @@ namespace ts { return token = SyntaxKind.GreaterThanToken; case CharacterCodes.question: pos++; + if (text.charCodeAt(pos) === CharacterCodes.dot && !isDigit(text.charCodeAt(pos + 1))) { + pos++; + return token = SyntaxKind.QuestionDotToken; + } return token = SyntaxKind.QuestionToken; case CharacterCodes.openBracket: pos++; diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 80debb46f8a04..c51ad4119d297 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -1,6 +1,10 @@ /*@internal*/ namespace ts { export function transformESNext(context: TransformationContext) { + const { + hoistVariableDeclaration + } = context; + return chainBundle(transformSourceFile); function transformSourceFile(node: SourceFile) { @@ -16,9 +20,157 @@ namespace ts { return node; } switch (node.kind) { + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.CallExpression: + if (node.flags & NodeFlags.OptionalChain) { + const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false); + Debug.assertNotNode(updated, isSyntheticReference); + return updated; + } + // falls through default: return visitEachChild(node, visitor, context); } } + + function flattenChain(chain: OptionalChain) { + const links: OptionalChain[] = [chain]; + while (!chain.questionDotToken && !isTaggedTemplateExpression(chain)) { + chain = cast(chain.expression, isOptionalChain); + links.unshift(chain); + } + return { expression: chain.expression, chain: links }; + } + + function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean): Expression { + const expression = visitNonOptionalExpression(node.expression, captureThisArg); + if (isSyntheticReference(expression)) { + // `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` } + // `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` } + return createSyntheticReferenceExpression(updateParen(node, expression.expression), expression.thisArg); + } + return updateParen(node, expression); + } + + function visitNonOptionalPropertyAccessExpression(node: PropertyAccessExpression, captureThisArg: boolean): Expression { + if (isOptionalChain(node)) { + // If `node` is an optional chain, then it is the outermost chain of an optional expression. + return visitOptionalExpression(node, captureThisArg); + } + + let expression = visitNode(node.expression, visitor, isExpression); + Debug.assertNotNode(expression, isSyntheticReference); + + let thisArg: Expression | undefined; + if (captureThisArg) { + // `a.b` -> { expression: `(_a = a).b`, thisArg: `_a` } + thisArg = createTempVariable(hoistVariableDeclaration); + expression = createParen(createAssignment(thisArg, expression)); + } + + expression = updatePropertyAccess(node, expression, visitNode(node.name, visitor, isIdentifier)); + return thisArg ? createSyntheticReferenceExpression(expression, thisArg) : expression; + } + + function visitNonOptionalElementAccessExpression(node: ElementAccessExpression, captureThisArg: boolean): Expression { + if (isOptionalChain(node)) { + // If `node` is an optional chain, then it is the outermost chain of an optional expression. + return visitOptionalExpression(node, captureThisArg); + } + + let expression = visitNode(node.expression, visitor, isExpression); + Debug.assertNotNode(expression, isSyntheticReference); + + let thisArg: Expression | undefined; + if (captureThisArg) { + // `a[b]` -> { expression: `(_a = a)[b]`, thisArg: `_a` } + thisArg = createTempVariable(hoistVariableDeclaration); + expression = createParen(createAssignment(thisArg, expression)); + } + + expression = updateElementAccess(node, expression, visitNode(node.argumentExpression, visitor, isExpression)); + return thisArg ? createSyntheticReferenceExpression(expression, thisArg) : expression; + } + + function visitNonOptionalCallExpression(node: CallExpression, captureThisArg: boolean): Expression { + if (isOptionalChain(node)) { + // If `node` is an optional chain, then it is the outermost chain of an optional expression. + return visitOptionalExpression(node, captureThisArg); + } + return visitEachChild(node, visitor, context); + } + + function visitNonOptionalExpression(node: Expression, captureThisArg: boolean): Expression { + switch (node.kind) { + case SyntaxKind.ParenthesizedExpression: return visitNonOptionalParenthesizedExpression(node as ParenthesizedExpression, captureThisArg); + case SyntaxKind.PropertyAccessExpression: return visitNonOptionalPropertyAccessExpression(node as PropertyAccessExpression, captureThisArg); + case SyntaxKind.ElementAccessExpression: return visitNonOptionalElementAccessExpression(node as ElementAccessExpression, captureThisArg); + case SyntaxKind.CallExpression: return visitNonOptionalCallExpression(node as CallExpression, captureThisArg); + default: return visitNode(node, visitor, isExpression); + } + } + + function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean): Expression { + const { expression, chain } = flattenChain(node); + const left = visitNonOptionalExpression(expression, isCallChain(chain[0])); + const temp = createTempVariable(hoistVariableDeclaration); + const leftThisArg = isSyntheticReference(left) ? left.thisArg : undefined; + const leftExpression = isSyntheticReference(left) ? left.expression : left; + let rightExpression: Expression = temp; + let thisArg: Expression | undefined; + for (let i = 0; i < chain.length; i++) { + const segment = chain[i]; + switch (segment.kind) { + case SyntaxKind.PropertyAccessExpression: + if (i === chain.length - 1 && captureThisArg) { + thisArg = createTempVariable(hoistVariableDeclaration); + rightExpression = createParen(createAssignment(thisArg, rightExpression)); + } + rightExpression = createPropertyAccess( + rightExpression, + visitNode(segment.name, visitor, isIdentifier) + ); + break; + case SyntaxKind.ElementAccessExpression: + if (i === chain.length - 1 && captureThisArg) { + thisArg = createTempVariable(hoistVariableDeclaration); + rightExpression = createParen(createAssignment(thisArg, rightExpression)); + } + rightExpression = createElementAccess( + rightExpression, + visitNode(segment.argumentExpression, visitor, isExpression) + ); + break; + case SyntaxKind.CallExpression: + if (i === 0 && leftThisArg) { + rightExpression = createFunctionCall( + rightExpression, + leftThisArg, + visitNodes(segment.arguments, visitor, isExpression) + ); + } + else { + rightExpression = createCall( + rightExpression, + /*typeArguments*/ undefined, + visitNodes(segment.arguments, visitor, isExpression) + ); + } + break; + } + setOriginalNode(rightExpression, segment); + } + + const target = createConditional( + createLogicalOr( + createStrictEquality(createAssignment(temp, leftExpression), createNull()), + createStrictEquality(temp, createVoidZero()) + ), + createVoidZero(), + rightExpression + ); + return thisArg ? createSyntheticReferenceExpression(target, thisArg) : target; + } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4ac846fd5e115..cd0b9f2f6c886 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -152,6 +152,7 @@ namespace ts { DotDotDotToken, SemicolonToken, CommaToken, + QuestionDotToken, LessThanToken, LessThanSlashToken, GreaterThanToken, @@ -485,6 +486,7 @@ namespace ts { CommaListExpression, MergeDeclarationMarker, EndOfDeclarationMarker, + SyntheticReferenceExpression, // Enum value count Count, @@ -532,20 +534,21 @@ namespace ts { NestedNamespace = 1 << 2, // Namespace declaration Synthesized = 1 << 3, // Node was synthesized during transformation Namespace = 1 << 4, // Namespace declaration - ExportContext = 1 << 5, // Export context (initialized by binding) - ContainsThis = 1 << 6, // Interface contains references to "this" - HasImplicitReturn = 1 << 7, // If function implicitly returns on one of codepaths (initialized by binding) - HasExplicitReturn = 1 << 8, // If function has explicit reachable return on one of codepaths (initialized by binding) - GlobalAugmentation = 1 << 9, // Set if module declaration is an augmentation for the global scope - HasAsyncFunctions = 1 << 10, // If the file has async functions (initialized by binding) - DisallowInContext = 1 << 11, // If node was parsed in a context where 'in-expressions' are not allowed - YieldContext = 1 << 12, // If node was parsed in the 'yield' context created when parsing a generator - DecoratorContext = 1 << 13, // If node was parsed as part of a decorator - AwaitContext = 1 << 14, // If node was parsed in the 'await' context created when parsing an async function - ThisNodeHasError = 1 << 15, // If the parser encountered an error when parsing the code that created this node - JavaScriptFile = 1 << 16, // If node was parsed in a JavaScript - ThisNodeOrAnySubNodesHasError = 1 << 17, // If this node or any of its children had an error - HasAggregatedChildData = 1 << 18, // If we've computed data from children and cached it in this node + OptionalChain = 1 << 5, // Chained MemberExpression rooted to a pseudo-OptionalExpression + ExportContext = 1 << 6, // Export context (initialized by binding) + ContainsThis = 1 << 7, // Interface contains references to "this" + HasImplicitReturn = 1 << 8, // If function implicitly returns on one of codepaths (initialized by binding) + HasExplicitReturn = 1 << 9, // If function has explicit reachable return on one of codepaths (initialized by binding) + GlobalAugmentation = 1 << 10, // Set if module declaration is an augmentation for the global scope + HasAsyncFunctions = 1 << 11, // If the file has async functions (initialized by binding) + DisallowInContext = 1 << 12, // If node was parsed in a context where 'in-expressions' are not allowed + YieldContext = 1 << 13, // If node was parsed in the 'yield' context created when parsing a generator + DecoratorContext = 1 << 14, // If node was parsed as part of a decorator + AwaitContext = 1 << 15, // If node was parsed in the 'await' context created when parsing an async function + ThisNodeHasError = 1 << 16, // If the parser encountered an error when parsing the code that created this node + JavaScriptFile = 1 << 17, // If node was parsed in a JavaScript + ThisNodeOrAnySubNodesHasError = 1 << 18, // If this node or any of its children had an error + HasAggregatedChildData = 1 << 19, // If we've computed data from children and cached it in this node // These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid // walking the tree if the flags are not set. However, these flags are just a approximation @@ -556,13 +559,13 @@ namespace ts { // removal, it is likely that users will add the import anyway. // The advantage of this approach is its simplicity. For the case of batch compilation, // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. - /* @internal */ PossiblyContainsDynamicImport = 1 << 19, - /* @internal */ PossiblyContainsImportMeta = 1 << 20, + /* @internal */ PossiblyContainsDynamicImport = 1 << 20, + /* @internal */ PossiblyContainsImportMeta = 1 << 21, - JSDoc = 1 << 21, // If node was parsed inside jsdoc - /* @internal */ Ambient = 1 << 22, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. - /* @internal */ InWithStatement = 1 << 23, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) - JsonFile = 1 << 24, // If node was parsed in a Json + JSDoc = 1 << 22, // If node was parsed inside jsdoc + /* @internal */ Ambient = 1 << 23, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. + /* @internal */ InWithStatement = 1 << 24, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) + JsonFile = 1 << 25, // If node was parsed in a Json BlockScoped = Let | Const, @@ -732,8 +735,10 @@ namespace ts { kind: TKind; } + export type DotToken = Token; export type DotDotDotToken = Token; export type QuestionToken = Token; + export type QuestionDotToken = Token; export type ExclamationToken = Token; export type ColonToken = Token; export type EqualsToken = Token; @@ -1803,9 +1808,19 @@ namespace ts { export interface PropertyAccessExpression extends MemberExpression, NamedDeclaration { kind: SyntaxKind.PropertyAccessExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; name: Identifier; } + export interface PropertyAccessChain extends PropertyAccessExpression { + _optionalChainBrand: any; + } + + /* @internal */ + export interface PropertyAccessChainRoot extends PropertyAccessChain { + questionDotToken: QuestionDotToken; + } + export interface SuperPropertyAccessExpression extends PropertyAccessExpression { expression: SuperExpression; } @@ -1819,9 +1834,19 @@ namespace ts { export interface ElementAccessExpression extends MemberExpression { kind: SyntaxKind.ElementAccessExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; argumentExpression: Expression; } + export interface ElementAccessChain extends ElementAccessExpression { + _optionalChainBrand: any; + } + + /* @internal */ + export interface ElementAccessChainRoot extends ElementAccessChain { + questionDotToken: QuestionDotToken; + } + export interface SuperElementAccessExpression extends ElementAccessExpression { expression: SuperExpression; } @@ -1832,10 +1857,33 @@ namespace ts { export interface CallExpression extends LeftHandSideExpression, Declaration { kind: SyntaxKind.CallExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; typeArguments?: NodeArray; arguments: NodeArray; } + export interface CallChain extends CallExpression { + _optionalChainBrand: any; + } + + /* @internal */ + export interface CallChainRoot extends CallChain { + questionDotToken: QuestionDotToken; + } + + export type OptionalChain = + | PropertyAccessChain + | ElementAccessChain + | CallChain + ; + + /* @internal */ + export type OptionalChainRoot = + | PropertyAccessChainRoot + | ElementAccessChainRoot + | CallChainRoot + ; + /** @internal */ export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: EntityNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } }; @@ -1866,6 +1914,7 @@ namespace ts { tag: LeftHandSideExpression; typeArguments?: NodeArray; template: TemplateLiteral; + /*@internal*/ questionDotToken?: QuestionDotToken; // NOTE: Invalid syntax, only used to report a grammar error. } export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement; @@ -2033,6 +2082,13 @@ namespace ts { kind: SyntaxKind.MergeDeclarationMarker; } + /* @internal */ + export interface SyntheticReferenceExpression extends LeftHandSideExpression { + kind: SyntaxKind.SyntheticReferenceExpression; + expression: Expression; + thisArg: Expression; + } + export interface EmptyStatement extends Statement { kind: SyntaxKind.EmptyStatement; } @@ -2603,8 +2659,9 @@ namespace ts { AfterFinally = 1 << 13, // Injected edge that links post-finally flow with the rest of the graph /** @internal */ Cached = 1 << 14, // Indicates that at least one cross-call cache entry exists for this node, even if not a loop participant + Label = BranchLabel | LoopLabel, - Condition = TrueCondition | FalseCondition + Condition = TrueCondition | FalseCondition, } export type FlowNode = @@ -3209,6 +3266,8 @@ namespace ts { /* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type; getNullableType(type: Type, flags: TypeFlags): Type; getNonNullableType(type: Type): Type; + /* @internal */ getNonOptionalType(type: Type): Type; + /* @internal */ isNullableType(type: Type): boolean; getTypeArguments(type: TypeReference): readonly Type[]; // TODO: GH#18217 `xToDeclaration` calls are frequently asserted as defined. @@ -3329,6 +3388,7 @@ namespace ts { /* @internal */ getNullType(): Type; /* @internal */ getESSymbolType(): Type; /* @internal */ getNeverType(): Type; + /* @internal */ getOptionalType(): Type; /* @internal */ getUnionType(types: Type[], subtypeReduction?: UnionReduction): Type; /* @internal */ createArrayType(elementType: Type): Type; /* @internal */ getElementTypeOfArrayType(arrayType: Type): Type | undefined; @@ -4590,6 +4650,8 @@ namespace ts { isolatedSignatureType?: ObjectType; // A manufactured type that just contains the signature for purposes of signature comparison /* @internal */ instantiations?: Map; // Generic signature instantiation cache + /* @internal */ + isOptionalCall?: boolean; } export const enum IndexKind { @@ -6032,6 +6094,8 @@ namespace ts { getColumn(): number; getIndent(): number; isAtStartOfLine(): boolean; + hasTrailingComment(): boolean; + hasTrailingWhitespace(): boolean; getTextPosWithWriteLine?(): number; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 1c7143f3705f7..6aed9b7f6aa31 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -57,7 +57,6 @@ namespace ts { function createSingleLineStringWriter(): EmitTextWriter { let str = ""; - const writeText: (text: string) => void = text => str += text; return { getText: () => str, @@ -79,6 +78,8 @@ namespace ts { getColumn: () => 0, getIndent: () => 0, isAtStartOfLine: () => false, + hasTrailingComment: () => false, + hasTrailingWhitespace: () => !!str.length && isWhiteSpaceLike(str.charCodeAt(str.length - 1)), // Completely ignore indentation for string writers. And map newlines to // a single space. @@ -3349,6 +3350,7 @@ namespace ts { let lineStart: boolean; let lineCount: number; let linePos: number; + let hasTrailingComment = false; function updateLineCountAndPosFor(s: string) { const lineStartsOfS = computeLineStarts(s); @@ -3362,7 +3364,7 @@ namespace ts { } } - function write(s: string) { + function writeText(s: string) { if (s && s.length) { if (lineStart) { s = getIndentString(indent) + s; @@ -3373,18 +3375,30 @@ namespace ts { } } + function write(s: string) { + if (s) hasTrailingComment = false; + writeText(s); + } + + function writeComment(s: string) { + if (s) hasTrailingComment = true; + writeText(s); + } + function reset(): void { output = ""; indent = 0; lineStart = true; lineCount = 0; linePos = 0; + hasTrailingComment = false; } function rawWrite(s: string) { if (s !== undefined) { output += s; updateLineCountAndPosFor(s); + hasTrailingComment = false; } } @@ -3400,6 +3414,7 @@ namespace ts { lineCount++; linePos = output.length; lineStart = true; + hasTrailingComment = false; } } @@ -3422,6 +3437,8 @@ namespace ts { getColumn: () => lineStart ? indent * getIndentSize() : output.length - linePos, getText: () => output, isAtStartOfLine: () => lineStart, + hasTrailingComment: () => hasTrailingComment, + hasTrailingWhitespace: () => !!output.length && isWhiteSpaceLike(output.charCodeAt(output.length - 1)), clear: reset, reportInaccessibleThisError: noop, reportPrivateInBaseOfClassExpression: noop, @@ -3436,7 +3453,7 @@ namespace ts { writeStringLiteral: write, writeSymbol: (s, _) => write(s), writeTrailingSemicolon: write, - writeComment: write, + writeComment, getTextPosWithWriteLine }; } @@ -4792,6 +4809,10 @@ namespace ts { return false; } } + + export function getDotOrQuestionDotToken(node: PropertyAccessExpression) { + return node.questionDotToken || createNode(SyntaxKind.DotToken, node.expression.end, node.name.pos) as DotToken; + } } namespace ts { @@ -5807,14 +5828,32 @@ namespace ts { return node.kind === SyntaxKind.PropertyAccessExpression; } + export function isPropertyAccessChain(node: Node): node is PropertyAccessChain { + return isPropertyAccessExpression(node) && !!(node.flags & NodeFlags.OptionalChain); + } + export function isElementAccessExpression(node: Node): node is ElementAccessExpression { return node.kind === SyntaxKind.ElementAccessExpression; } + export function isElementAccessChain(node: Node): node is ElementAccessChain { + return isElementAccessExpression(node) && !!(node.flags & NodeFlags.OptionalChain); + } + export function isCallExpression(node: Node): node is CallExpression { return node.kind === SyntaxKind.CallExpression; } + export function isCallChain(node: Node): node is CallChain { + return isCallExpression(node) && !!(node.flags & NodeFlags.OptionalChain); + } + + export function isOptionalChain(node: Node): node is PropertyAccessChain | ElementAccessChain | CallChain { + return isPropertyAccessChain(node) + || isElementAccessChain(node) + || isCallChain(node); + } + export function isNewExpression(node: Node): node is NewExpression { return node.kind === SyntaxKind.NewExpression; } @@ -6823,6 +6862,11 @@ namespace ts { return node.kind === SyntaxKind.NotEmittedStatement; } + /* @internal */ + export function isSyntheticReference(node: Node): node is SyntheticReferenceExpression { + return node.kind === SyntaxKind.SyntheticReferenceExpression; + } + /* @internal */ export function isNotEmittedOrPartiallyEmittedNode(node: Node): node is NotEmittedStatement | PartiallyEmittedExpression { return isNotEmittedStatement(node) @@ -7127,6 +7171,11 @@ namespace ts { return node.kind === SyntaxKind.GetAccessor; } + /* @internal */ + export function isOptionalChainRoot(node: Node): node is OptionalChainRoot { + return isOptionalChain(node) && !!node.questionDotToken; + } + /** True if has jsdoc nodes attached to it. */ /* @internal */ // TODO: GH#19856 Would like to return `node is Node & { jsDoc: JSDoc[] }` but it causes long compile times diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 3ef382cd10fd8..4f36ccb1b900f 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -456,16 +456,35 @@ namespace ts { nodesVisitor((node).properties, visitor, isObjectLiteralElementLike)); case SyntaxKind.PropertyAccessExpression: + if (node.flags & NodeFlags.OptionalChain) { + return updatePropertyAccessChain(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).questionDotToken, visitor, isToken), + visitNode((node).name, visitor, isIdentifier)); + } return updatePropertyAccess(node, visitNode((node).expression, visitor, isExpression), visitNode((node).name, visitor, isIdentifier)); case SyntaxKind.ElementAccessExpression: + if (node.flags & NodeFlags.OptionalChain) { + return updateElementAccessChain(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).questionDotToken, visitor, isToken), + visitNode((node).argumentExpression, visitor, isExpression)); + } return updateElementAccess(node, visitNode((node).expression, visitor, isExpression), visitNode((node).argumentExpression, visitor, isExpression)); case SyntaxKind.CallExpression: + if (node.flags & NodeFlags.OptionalChain) { + return updateCallChain(node, + visitNode((node).expression, visitor, isExpression), + visitNode((node).questionDotToken, visitor, isToken), + nodesVisitor((node).typeArguments, visitor, isTypeNode), + nodesVisitor((node).arguments, visitor, isExpression)); + } return updateCall(node, visitNode((node).expression, visitor, isExpression), nodesVisitor((node).typeArguments, visitor, isTypeNode), diff --git a/src/services/completions.ts b/src/services/completions.ts index de46b1e0a0c1d..1c3288a32828a 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -11,28 +11,52 @@ namespace ts.Completions { } export type Log = (message: string) => void; - const enum SymbolOriginInfoKind { ThisType, SymbolMemberNoExport, SymbolMemberExport, Export, Promise } - type SymbolOriginInfo = { kind: SymbolOriginInfoKind.ThisType } | { kind: SymbolOriginInfoKind.Promise } | { kind: SymbolOriginInfoKind.SymbolMemberNoExport } | SymbolOriginInfoExport; - interface SymbolOriginInfoExport { - kind: SymbolOriginInfoKind.SymbolMemberExport | SymbolOriginInfoKind.Export; + const enum SymbolOriginInfoKind { + ThisType = 1 << 0, + SymbolMember = 1 << 1, + Export = 1 << 2, + Promise = 1 << 3, + Nullable = 1 << 4, + + SymbolMemberNoExport = SymbolMember, + SymbolMemberExport = SymbolMember | Export, + } + + interface SymbolOriginInfo { + kind: SymbolOriginInfoKind; + } + + interface SymbolOriginInfoExport extends SymbolOriginInfo { + kind: SymbolOriginInfoKind; moduleSymbol: Symbol; isDefaultExport: boolean; } + + function originIsThisType(origin: SymbolOriginInfo): boolean { + return !!(origin.kind & SymbolOriginInfoKind.ThisType); + } + function originIsSymbolMember(origin: SymbolOriginInfo): boolean { - return origin.kind === SymbolOriginInfoKind.SymbolMemberExport || origin.kind === SymbolOriginInfoKind.SymbolMemberNoExport; + return !!(origin.kind & SymbolOriginInfoKind.SymbolMember); } + function originIsExport(origin: SymbolOriginInfo): origin is SymbolOriginInfoExport { - return origin.kind === SymbolOriginInfoKind.SymbolMemberExport || origin.kind === SymbolOriginInfoKind.Export; + return !!(origin.kind & SymbolOriginInfoKind.Export); } + function originIsPromise(origin: SymbolOriginInfo): boolean { - return origin.kind === SymbolOriginInfoKind.Promise; + return !!(origin.kind & SymbolOriginInfoKind.Promise); + } + + function originIsNullableMember(origin: SymbolOriginInfo): boolean { + return !!(origin.kind & SymbolOriginInfoKind.Nullable); } /** * Map from symbol id -> SymbolOriginInfo. * Only populated for symbols that come from other modules. */ - type SymbolOriginInfoMap = (SymbolOriginInfo | undefined)[]; + type SymbolOriginInfoMap = (SymbolOriginInfo | SymbolOriginInfoExport | undefined)[]; type SymbolSortTextMap = (SortText | undefined)[]; @@ -314,14 +338,26 @@ namespace ts.Completions { ): CompletionEntry | undefined { let insertText: string | undefined; let replacementSpan: TextSpan | undefined; - if (origin && origin.kind === SymbolOriginInfoKind.ThisType) { - insertText = needsConvertPropertyAccess ? `this[${quote(name, preferences)}]` : `this.${name}`; + const insertQuestionDot = origin && originIsNullableMember(origin); + const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess; + if (origin && originIsThisType(origin)) { + insertText = needsConvertPropertyAccess + ? `this${insertQuestionDot ? "?." : ""}[${quote(name, preferences)}]` + : `this${insertQuestionDot ? "?." : "."}${name}`; } // We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790. // Somehow there was a global with a non-identifier name. Hopefully someone will complain about getting a "foo bar" global completion and provide a repro. - else if ((origin && originIsSymbolMember(origin) || needsConvertPropertyAccess) && propertyAccessToConvert) { - insertText = needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]`; - const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile)!; + else if ((useBraces || insertQuestionDot) && propertyAccessToConvert) { + insertText = useBraces ? needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]` : name; + if (insertQuestionDot || propertyAccessToConvert.questionDotToken) { + insertText = `?.${insertText}`; + } + + const dot = findChildOfKind(propertyAccessToConvert, SyntaxKind.DotToken, sourceFile) || + findChildOfKind(propertyAccessToConvert, SyntaxKind.QuestionDotToken, sourceFile); + if (!dot) { + return undefined; + } // If the text after the '.' starts with this name, write over it. Else, add new text. const end = startsWith(name, propertyAccessToConvert.name.text) ? propertyAccessToConvert.name.end : dot.end; replacementSpan = createTextSpanFromBounds(dot.getStart(sourceFile), end); @@ -337,7 +373,7 @@ namespace ts.Completions { if (origin && originIsPromise(origin) && propertyAccessToConvert) { if (insertText === undefined) insertText = name; const awaitText = `(await ${propertyAccessToConvert.expression.getText()})`; - insertText = needsConvertPropertyAccess ? `${awaitText}${insertText}` : `${awaitText}.${insertText}`; + insertText = needsConvertPropertyAccess ? `${awaitText}${insertText}` : `${awaitText}${insertQuestionDot ? "?." : "."}${insertText}`; replacementSpan = createTextSpanFromBounds(propertyAccessToConvert.getStart(sourceFile), propertyAccessToConvert.end); } @@ -846,6 +882,7 @@ namespace ts.Completions { let node = currentToken; let propertyAccessToConvert: PropertyAccessExpression | undefined; let isRightOfDot = false; + let isRightOfQuestionDot = false; let isRightOfOpenTag = false; let isStartingCloseTag = false; let isJsxInitializer: IsJsxInitializer = false; @@ -859,8 +896,9 @@ namespace ts.Completions { } let parent = contextToken.parent; - if (contextToken.kind === SyntaxKind.DotToken) { - isRightOfDot = true; + if (contextToken.kind === SyntaxKind.DotToken || contextToken.kind === SyntaxKind.QuestionDotToken) { + isRightOfDot = contextToken.kind === SyntaxKind.DotToken; + isRightOfQuestionDot = contextToken.kind === SyntaxKind.QuestionDotToken; switch (parent.kind) { case SyntaxKind.PropertyAccessExpression: propertyAccessToConvert = parent as PropertyAccessExpression; @@ -967,7 +1005,7 @@ namespace ts.Completions { const symbolToSortTextMap: SymbolSortTextMap = []; const importSuggestionsCache = host.getImportSuggestionsCache && host.getImportSuggestionsCache(); - if (isRightOfDot) { + if (isRightOfDot || isRightOfQuestionDot) { getTypeScriptMemberSymbols(); } else if (isRightOfOpenTag) { @@ -1074,7 +1112,13 @@ namespace ts.Completions { if (!isTypeLocation && symbol.declarations && symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) { - addTypeProperties(typeChecker.getTypeOfSymbolAtLocation(symbol, node), !!(node.flags & NodeFlags.AwaitContext)); + let type = typeChecker.getTypeOfSymbolAtLocation(symbol, node).getNonOptionalType(); + let insertQuestionDot = false; + if (type.isNullableType()) { + insertQuestionDot = isRightOfDot && !isRightOfQuestionDot; + type = type.getNonNullableType(); + } + addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot); } return; @@ -1089,12 +1133,21 @@ namespace ts.Completions { } if (!isTypeLocation) { - addTypeProperties(typeChecker.getTypeAtLocation(node), !!(node.flags & NodeFlags.AwaitContext)); + let type = typeChecker.getTypeAtLocation(node).getNonOptionalType(); + let insertQuestionDot = false; + if (type.isNullableType()) { + insertQuestionDot = isRightOfDot && !isRightOfQuestionDot; + type = type.getNonNullableType(); + } + addTypeProperties(type, !!(node.flags & NodeFlags.AwaitContext), insertQuestionDot); } } - function addTypeProperties(type: Type, insertAwait?: boolean): void { + function addTypeProperties(type: Type, insertAwait: boolean, insertQuestionDot: boolean): void { isNewIdentifierLocation = !!type.getStringIndexType(); + if (isRightOfQuestionDot && some(type.getCallSignatures())) { + isNewIdentifierLocation = true; + } const propertyAccess = node.kind === SyntaxKind.ImportType ? node : node.parent; if (isUncheckedFile) { @@ -1108,7 +1161,7 @@ namespace ts.Completions { else { for (const symbol of type.getApparentProperties()) { if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, symbol)) { - addPropertySymbol(symbol); + addPropertySymbol(symbol, /*insertAwait*/ false, insertQuestionDot); } } } @@ -1118,14 +1171,14 @@ namespace ts.Completions { if (promiseType) { for (const symbol of promiseType.getApparentProperties()) { if (typeChecker.isValidPropertyAccessForCompletions(propertyAccess, promiseType, symbol)) { - addPropertySymbol(symbol, /* insertAwait */ true); + addPropertySymbol(symbol, /* insertAwait */ true, insertQuestionDot); } } } } } - function addPropertySymbol(symbol: Symbol, insertAwait?: boolean) { + function addPropertySymbol(symbol: Symbol, insertAwait: boolean, insertQuestionDot: boolean) { // For a computed property with an accessible name like `Symbol.iterator`, // we'll add a completion for the *name* `Symbol` instead of for the property. // If this is e.g. [Symbol.iterator], add a completion for `Symbol`. @@ -1139,23 +1192,34 @@ namespace ts.Completions { symbols.push(firstAccessibleSymbol); const moduleSymbol = firstAccessibleSymbol.parent; symbolToOriginInfoMap[getSymbolId(firstAccessibleSymbol)] = - !moduleSymbol || !isExternalModuleSymbol(moduleSymbol) ? { kind: SymbolOriginInfoKind.SymbolMemberNoExport } : { kind: SymbolOriginInfoKind.SymbolMemberExport, moduleSymbol, isDefaultExport: false }; + !moduleSymbol || !isExternalModuleSymbol(moduleSymbol) + ? { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberNoExport) } + : { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.SymbolMemberExport), moduleSymbol, isDefaultExport: false }; } else if (preferences.includeCompletionsWithInsertText) { - addPromiseSymbolOriginInfo(symbol); + addSymbolOriginInfo(symbol); symbols.push(symbol); } } else { - addPromiseSymbolOriginInfo(symbol); + addSymbolOriginInfo(symbol); symbols.push(symbol); } - function addPromiseSymbolOriginInfo (symbol: Symbol) { - if (insertAwait && preferences.includeCompletionsWithInsertText && !symbolToOriginInfoMap[getSymbolId(symbol)]) { - symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.Promise }; + function addSymbolOriginInfo(symbol: Symbol) { + if (preferences.includeCompletionsWithInsertText) { + if (insertAwait && !symbolToOriginInfoMap[getSymbolId(symbol)]) { + symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: getNullableSymbolOriginInfoKind(SymbolOriginInfoKind.Promise) }; + } + else if (insertQuestionDot) { + symbolToOriginInfoMap[getSymbolId(symbol)] = { kind: SymbolOriginInfoKind.Nullable }; + } } } + + function getNullableSymbolOriginInfoKind(kind: SymbolOriginInfoKind) { + return insertQuestionDot ? kind | SymbolOriginInfoKind.Nullable : kind; + } } /** Given 'a.b.c', returns 'a'. */ diff --git a/src/services/services.ts b/src/services/services.ts index 0cff17fffb31f..6a1c721cbba30 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -409,9 +409,15 @@ namespace ts { getBaseTypes(): BaseType[] | undefined { return this.isClassOrInterface() ? this.checker.getBaseTypes(this) : undefined; } + isNullableType(): boolean { + return this.checker.isNullableType(this); + } getNonNullableType(): Type { return this.checker.getNonNullableType(this); } + getNonOptionalType(): Type { + return this.checker.getNonOptionalType(this); + } getConstraint(): Type | undefined { return this.checker.getBaseConstraintOfType(this); } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 03e7a1c458d33..ad68a8ff5c880 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -1061,6 +1061,8 @@ namespace ts.textChanges { getColumn, getIndent, isAtStartOfLine, + hasTrailingComment: () => writer.hasTrailingComment(), + hasTrailingWhitespace: () => writer.hasTrailingWhitespace(), clear }; } diff --git a/src/services/types.ts b/src/services/types.ts index 5051a977fed9a..65544d5677d62 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -52,6 +52,8 @@ namespace ts { getNumberIndexType(): Type | undefined; getBaseTypes(): BaseType[] | undefined; getNonNullableType(): Type; + /*@internal*/ getNonOptionalType(): Type; + /*@internal*/ isNullableType(): boolean; getConstraint(): Type | undefined; getDefault(): Type | undefined; diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 1973972f313ad..cea78be7151a5 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -956,6 +956,12 @@ namespace ts { } } + export function removeOptionality(type: Type, isOptionalExpression: boolean, isOptionalChain: boolean) { + return isOptionalExpression ? type.getNonNullableType() : + isOptionalChain ? type.getNonOptionalType() : + type; + } + export function isPossiblyTypeArgumentPosition(token: Node, sourceFile: SourceFile, checker: TypeChecker): boolean { const info = getPossibleTypeArgumentsInfo(token, sourceFile); return info !== undefined && (isPartOfTypeNode(info.called) || @@ -964,7 +970,11 @@ namespace ts { } export function getPossibleGenericSignatures(called: Expression, typeArgumentCount: number, checker: TypeChecker): readonly Signature[] { - const type = checker.getTypeAtLocation(called); + let type = checker.getTypeAtLocation(called); + if (isOptionalChain(called.parent)) { + type = removeOptionality(type, !!called.parent.questionDotToken, /*isOptionalChain*/ true); + } + const signatures = isNewExpression(called.parent) ? type.getConstructSignatures() : type.getCallSignatures(); return signatures.filter(candidate => !!candidate.typeParameters && candidate.typeParameters.length >= typeArgumentCount); } @@ -993,6 +1003,9 @@ namespace ts { case SyntaxKind.LessThanToken: // Found the beginning of the generic argument expression token = findPrecedingToken(token.getFullStart(), sourceFile); + if (token && token.kind === SyntaxKind.QuestionDotToken) { + token = findPrecedingToken(token.getFullStart(), sourceFile); + } if (!token || !isIdentifier(token)) return undefined; if (!remainingLessThanTokens) { return isDeclarationName(token) ? undefined : { called: token, nTypeArguments }; @@ -1493,6 +1506,8 @@ namespace ts { getColumn: () => 0, getLine: () => 0, isAtStartOfLine: () => false, + hasTrailingWhitespace: () => false, + hasTrailingComment: () => false, rawWrite: notImplemented, getIndent: () => indent, increaseIndent: () => { indent++; }, diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 9614941ce7495..1892c4c481539 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -76,6 +76,7 @@ "unittests/evaluation/awaiter.ts", "unittests/evaluation/forAwaitOf.ts", "unittests/evaluation/forOf.ts", + "unittests/evaluation/optionalCall.ts", "unittests/evaluation/objectRest.ts", "unittests/services/cancellableLanguageServiceOperations.ts", "unittests/services/colorization.ts", diff --git a/src/testRunner/unittests/evaluation/optionalCall.ts b/src/testRunner/unittests/evaluation/optionalCall.ts new file mode 100644 index 0000000000000..d6317ad0a8ca9 --- /dev/null +++ b/src/testRunner/unittests/evaluation/optionalCall.ts @@ -0,0 +1,191 @@ +describe("unittests:: evaluation:: optionalCall", () => { + it("f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + function f(a) { + output.push(a); + output.push(this); + } + export const output: any[] = []; + f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.isUndefined(result.output[1]); + }); + it("o.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + export const output: any[] = []; + o.f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o); + }); + it("o.x.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o.x.f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("o?.f()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + export const output: any[] = []; + o?.f(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o); + }); + it("o?.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + export const output: any[] = []; + o?.f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o); + }); + it("o.x?.f()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o.x?.f(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("o?.x.f()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o?.x.f(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("o?.x?.f()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o?.x?.f(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("o?.x?.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + x: { + f(a) { + output.push(a); + output.push(this); + } + } + }; + export const output: any[] = []; + o?.x?.f?.(1); + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], result.o.x); + }); + it("f?.()?.()", async () => { + const result = evaluator.evaluateTypeScript(` + function g(a) { + output.push(a); + output.push(this); + } + function f(a) { + output.push(a); + return g; + } + export const output: any[] = []; + f?.(1)?.(2) + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], 2); + assert.isUndefined(result.output[2]); + }); + it("f?.().f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + function f(a) { + output.push(a); + return o; + } + export const output: any[] = []; + f?.(1).f?.(2) + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], 2); + assert.strictEqual(result.output[2], result.o); + }); + it("f?.()?.f?.()", async () => { + const result = evaluator.evaluateTypeScript(` + export const o = { + f(a) { + output.push(a); + output.push(this); + } + }; + function f(a) { + output.push(a); + return o; + } + export const output: any[] = []; + f?.(1)?.f?.(2) + `); + assert.strictEqual(result.output[0], 1); + assert.strictEqual(result.output[1], 2); + assert.strictEqual(result.output[2], result.o); + }); +}); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index c705080bc038a..1878347bfac74 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -104,332 +104,334 @@ declare namespace ts { DotDotDotToken = 25, SemicolonToken = 26, CommaToken = 27, - LessThanToken = 28, - LessThanSlashToken = 29, - GreaterThanToken = 30, - LessThanEqualsToken = 31, - GreaterThanEqualsToken = 32, - EqualsEqualsToken = 33, - ExclamationEqualsToken = 34, - EqualsEqualsEqualsToken = 35, - ExclamationEqualsEqualsToken = 36, - EqualsGreaterThanToken = 37, - PlusToken = 38, - MinusToken = 39, - AsteriskToken = 40, - AsteriskAsteriskToken = 41, - SlashToken = 42, - PercentToken = 43, - PlusPlusToken = 44, - MinusMinusToken = 45, - LessThanLessThanToken = 46, - GreaterThanGreaterThanToken = 47, - GreaterThanGreaterThanGreaterThanToken = 48, - AmpersandToken = 49, - BarToken = 50, - CaretToken = 51, - ExclamationToken = 52, - TildeToken = 53, - AmpersandAmpersandToken = 54, - BarBarToken = 55, - QuestionToken = 56, - ColonToken = 57, - AtToken = 58, + QuestionDotToken = 28, + LessThanToken = 29, + LessThanSlashToken = 30, + GreaterThanToken = 31, + LessThanEqualsToken = 32, + GreaterThanEqualsToken = 33, + EqualsEqualsToken = 34, + ExclamationEqualsToken = 35, + EqualsEqualsEqualsToken = 36, + ExclamationEqualsEqualsToken = 37, + EqualsGreaterThanToken = 38, + PlusToken = 39, + MinusToken = 40, + AsteriskToken = 41, + AsteriskAsteriskToken = 42, + SlashToken = 43, + PercentToken = 44, + PlusPlusToken = 45, + MinusMinusToken = 46, + LessThanLessThanToken = 47, + GreaterThanGreaterThanToken = 48, + GreaterThanGreaterThanGreaterThanToken = 49, + AmpersandToken = 50, + BarToken = 51, + CaretToken = 52, + ExclamationToken = 53, + TildeToken = 54, + AmpersandAmpersandToken = 55, + BarBarToken = 56, + QuestionToken = 57, + ColonToken = 58, + AtToken = 59, /** Only the JSDoc scanner produces BacktickToken. The normal scanner produces NoSubstitutionTemplateLiteral and related kinds. */ - BacktickToken = 59, - EqualsToken = 60, - PlusEqualsToken = 61, - MinusEqualsToken = 62, - AsteriskEqualsToken = 63, - AsteriskAsteriskEqualsToken = 64, - SlashEqualsToken = 65, - PercentEqualsToken = 66, - LessThanLessThanEqualsToken = 67, - GreaterThanGreaterThanEqualsToken = 68, - GreaterThanGreaterThanGreaterThanEqualsToken = 69, - AmpersandEqualsToken = 70, - BarEqualsToken = 71, - CaretEqualsToken = 72, - Identifier = 73, - BreakKeyword = 74, - CaseKeyword = 75, - CatchKeyword = 76, - ClassKeyword = 77, - ConstKeyword = 78, - ContinueKeyword = 79, - DebuggerKeyword = 80, - DefaultKeyword = 81, - DeleteKeyword = 82, - DoKeyword = 83, - ElseKeyword = 84, - EnumKeyword = 85, - ExportKeyword = 86, - ExtendsKeyword = 87, - FalseKeyword = 88, - FinallyKeyword = 89, - ForKeyword = 90, - FunctionKeyword = 91, - IfKeyword = 92, - ImportKeyword = 93, - InKeyword = 94, - InstanceOfKeyword = 95, - NewKeyword = 96, - NullKeyword = 97, - ReturnKeyword = 98, - SuperKeyword = 99, - SwitchKeyword = 100, - ThisKeyword = 101, - ThrowKeyword = 102, - TrueKeyword = 103, - TryKeyword = 104, - TypeOfKeyword = 105, - VarKeyword = 106, - VoidKeyword = 107, - WhileKeyword = 108, - WithKeyword = 109, - ImplementsKeyword = 110, - InterfaceKeyword = 111, - LetKeyword = 112, - PackageKeyword = 113, - PrivateKeyword = 114, - ProtectedKeyword = 115, - PublicKeyword = 116, - StaticKeyword = 117, - YieldKeyword = 118, - AbstractKeyword = 119, - AsKeyword = 120, - AssertsKeyword = 121, - AnyKeyword = 122, - AsyncKeyword = 123, - AwaitKeyword = 124, - BooleanKeyword = 125, - ConstructorKeyword = 126, - DeclareKeyword = 127, - GetKeyword = 128, - InferKeyword = 129, - IsKeyword = 130, - KeyOfKeyword = 131, - ModuleKeyword = 132, - NamespaceKeyword = 133, - NeverKeyword = 134, - ReadonlyKeyword = 135, - RequireKeyword = 136, - NumberKeyword = 137, - ObjectKeyword = 138, - SetKeyword = 139, - StringKeyword = 140, - SymbolKeyword = 141, - TypeKeyword = 142, - UndefinedKeyword = 143, - UniqueKeyword = 144, - UnknownKeyword = 145, - FromKeyword = 146, - GlobalKeyword = 147, - BigIntKeyword = 148, - OfKeyword = 149, - QualifiedName = 150, - ComputedPropertyName = 151, - TypeParameter = 152, - Parameter = 153, - Decorator = 154, - PropertySignature = 155, - PropertyDeclaration = 156, - MethodSignature = 157, - MethodDeclaration = 158, - Constructor = 159, - GetAccessor = 160, - SetAccessor = 161, - CallSignature = 162, - ConstructSignature = 163, - IndexSignature = 164, - TypePredicate = 165, - TypeReference = 166, - FunctionType = 167, - ConstructorType = 168, - TypeQuery = 169, - TypeLiteral = 170, - ArrayType = 171, - TupleType = 172, - OptionalType = 173, - RestType = 174, - UnionType = 175, - IntersectionType = 176, - ConditionalType = 177, - InferType = 178, - ParenthesizedType = 179, - ThisType = 180, - TypeOperator = 181, - IndexedAccessType = 182, - MappedType = 183, - LiteralType = 184, - ImportType = 185, - ObjectBindingPattern = 186, - ArrayBindingPattern = 187, - BindingElement = 188, - ArrayLiteralExpression = 189, - ObjectLiteralExpression = 190, - PropertyAccessExpression = 191, - ElementAccessExpression = 192, - CallExpression = 193, - NewExpression = 194, - TaggedTemplateExpression = 195, - TypeAssertionExpression = 196, - ParenthesizedExpression = 197, - FunctionExpression = 198, - ArrowFunction = 199, - DeleteExpression = 200, - TypeOfExpression = 201, - VoidExpression = 202, - AwaitExpression = 203, - PrefixUnaryExpression = 204, - PostfixUnaryExpression = 205, - BinaryExpression = 206, - ConditionalExpression = 207, - TemplateExpression = 208, - YieldExpression = 209, - SpreadElement = 210, - ClassExpression = 211, - OmittedExpression = 212, - ExpressionWithTypeArguments = 213, - AsExpression = 214, - NonNullExpression = 215, - MetaProperty = 216, - SyntheticExpression = 217, - TemplateSpan = 218, - SemicolonClassElement = 219, - Block = 220, - EmptyStatement = 221, - VariableStatement = 222, - ExpressionStatement = 223, - IfStatement = 224, - DoStatement = 225, - WhileStatement = 226, - ForStatement = 227, - ForInStatement = 228, - ForOfStatement = 229, - ContinueStatement = 230, - BreakStatement = 231, - ReturnStatement = 232, - WithStatement = 233, - SwitchStatement = 234, - LabeledStatement = 235, - ThrowStatement = 236, - TryStatement = 237, - DebuggerStatement = 238, - VariableDeclaration = 239, - VariableDeclarationList = 240, - FunctionDeclaration = 241, - ClassDeclaration = 242, - InterfaceDeclaration = 243, - TypeAliasDeclaration = 244, - EnumDeclaration = 245, - ModuleDeclaration = 246, - ModuleBlock = 247, - CaseBlock = 248, - NamespaceExportDeclaration = 249, - ImportEqualsDeclaration = 250, - ImportDeclaration = 251, - ImportClause = 252, - NamespaceImport = 253, - NamedImports = 254, - ImportSpecifier = 255, - ExportAssignment = 256, - ExportDeclaration = 257, - NamedExports = 258, - ExportSpecifier = 259, - MissingDeclaration = 260, - ExternalModuleReference = 261, - JsxElement = 262, - JsxSelfClosingElement = 263, - JsxOpeningElement = 264, - JsxClosingElement = 265, - JsxFragment = 266, - JsxOpeningFragment = 267, - JsxClosingFragment = 268, - JsxAttribute = 269, - JsxAttributes = 270, - JsxSpreadAttribute = 271, - JsxExpression = 272, - CaseClause = 273, - DefaultClause = 274, - HeritageClause = 275, - CatchClause = 276, - PropertyAssignment = 277, - ShorthandPropertyAssignment = 278, - SpreadAssignment = 279, - EnumMember = 280, - UnparsedPrologue = 281, - UnparsedPrepend = 282, - UnparsedText = 283, - UnparsedInternalText = 284, - UnparsedSyntheticReference = 285, - SourceFile = 286, - Bundle = 287, - UnparsedSource = 288, - InputFiles = 289, - JSDocTypeExpression = 290, - JSDocAllType = 291, - JSDocUnknownType = 292, - JSDocNullableType = 293, - JSDocNonNullableType = 294, - JSDocOptionalType = 295, - JSDocFunctionType = 296, - JSDocVariadicType = 297, - JSDocNamepathType = 298, - JSDocComment = 299, - JSDocTypeLiteral = 300, - JSDocSignature = 301, - JSDocTag = 302, - JSDocAugmentsTag = 303, - JSDocAuthorTag = 304, - JSDocClassTag = 305, - JSDocCallbackTag = 306, - JSDocEnumTag = 307, - JSDocParameterTag = 308, - JSDocReturnTag = 309, - JSDocThisTag = 310, - JSDocTypeTag = 311, - JSDocTemplateTag = 312, - JSDocTypedefTag = 313, - JSDocPropertyTag = 314, - SyntaxList = 315, - NotEmittedStatement = 316, - PartiallyEmittedExpression = 317, - CommaListExpression = 318, - MergeDeclarationMarker = 319, - EndOfDeclarationMarker = 320, - Count = 321, - FirstAssignment = 60, - LastAssignment = 72, - FirstCompoundAssignment = 61, - LastCompoundAssignment = 72, - FirstReservedWord = 74, - LastReservedWord = 109, - FirstKeyword = 74, - LastKeyword = 149, - FirstFutureReservedWord = 110, - LastFutureReservedWord = 118, - FirstTypeNode = 165, - LastTypeNode = 185, + BacktickToken = 60, + EqualsToken = 61, + PlusEqualsToken = 62, + MinusEqualsToken = 63, + AsteriskEqualsToken = 64, + AsteriskAsteriskEqualsToken = 65, + SlashEqualsToken = 66, + PercentEqualsToken = 67, + LessThanLessThanEqualsToken = 68, + GreaterThanGreaterThanEqualsToken = 69, + GreaterThanGreaterThanGreaterThanEqualsToken = 70, + AmpersandEqualsToken = 71, + BarEqualsToken = 72, + CaretEqualsToken = 73, + Identifier = 74, + BreakKeyword = 75, + CaseKeyword = 76, + CatchKeyword = 77, + ClassKeyword = 78, + ConstKeyword = 79, + ContinueKeyword = 80, + DebuggerKeyword = 81, + DefaultKeyword = 82, + DeleteKeyword = 83, + DoKeyword = 84, + ElseKeyword = 85, + EnumKeyword = 86, + ExportKeyword = 87, + ExtendsKeyword = 88, + FalseKeyword = 89, + FinallyKeyword = 90, + ForKeyword = 91, + FunctionKeyword = 92, + IfKeyword = 93, + ImportKeyword = 94, + InKeyword = 95, + InstanceOfKeyword = 96, + NewKeyword = 97, + NullKeyword = 98, + ReturnKeyword = 99, + SuperKeyword = 100, + SwitchKeyword = 101, + ThisKeyword = 102, + ThrowKeyword = 103, + TrueKeyword = 104, + TryKeyword = 105, + TypeOfKeyword = 106, + VarKeyword = 107, + VoidKeyword = 108, + WhileKeyword = 109, + WithKeyword = 110, + ImplementsKeyword = 111, + InterfaceKeyword = 112, + LetKeyword = 113, + PackageKeyword = 114, + PrivateKeyword = 115, + ProtectedKeyword = 116, + PublicKeyword = 117, + StaticKeyword = 118, + YieldKeyword = 119, + AbstractKeyword = 120, + AsKeyword = 121, + AssertsKeyword = 122, + AnyKeyword = 123, + AsyncKeyword = 124, + AwaitKeyword = 125, + BooleanKeyword = 126, + ConstructorKeyword = 127, + DeclareKeyword = 128, + GetKeyword = 129, + InferKeyword = 130, + IsKeyword = 131, + KeyOfKeyword = 132, + ModuleKeyword = 133, + NamespaceKeyword = 134, + NeverKeyword = 135, + ReadonlyKeyword = 136, + RequireKeyword = 137, + NumberKeyword = 138, + ObjectKeyword = 139, + SetKeyword = 140, + StringKeyword = 141, + SymbolKeyword = 142, + TypeKeyword = 143, + UndefinedKeyword = 144, + UniqueKeyword = 145, + UnknownKeyword = 146, + FromKeyword = 147, + GlobalKeyword = 148, + BigIntKeyword = 149, + OfKeyword = 150, + QualifiedName = 151, + ComputedPropertyName = 152, + TypeParameter = 153, + Parameter = 154, + Decorator = 155, + PropertySignature = 156, + PropertyDeclaration = 157, + MethodSignature = 158, + MethodDeclaration = 159, + Constructor = 160, + GetAccessor = 161, + SetAccessor = 162, + CallSignature = 163, + ConstructSignature = 164, + IndexSignature = 165, + TypePredicate = 166, + TypeReference = 167, + FunctionType = 168, + ConstructorType = 169, + TypeQuery = 170, + TypeLiteral = 171, + ArrayType = 172, + TupleType = 173, + OptionalType = 174, + RestType = 175, + UnionType = 176, + IntersectionType = 177, + ConditionalType = 178, + InferType = 179, + ParenthesizedType = 180, + ThisType = 181, + TypeOperator = 182, + IndexedAccessType = 183, + MappedType = 184, + LiteralType = 185, + ImportType = 186, + ObjectBindingPattern = 187, + ArrayBindingPattern = 188, + BindingElement = 189, + ArrayLiteralExpression = 190, + ObjectLiteralExpression = 191, + PropertyAccessExpression = 192, + ElementAccessExpression = 193, + CallExpression = 194, + NewExpression = 195, + TaggedTemplateExpression = 196, + TypeAssertionExpression = 197, + ParenthesizedExpression = 198, + FunctionExpression = 199, + ArrowFunction = 200, + DeleteExpression = 201, + TypeOfExpression = 202, + VoidExpression = 203, + AwaitExpression = 204, + PrefixUnaryExpression = 205, + PostfixUnaryExpression = 206, + BinaryExpression = 207, + ConditionalExpression = 208, + TemplateExpression = 209, + YieldExpression = 210, + SpreadElement = 211, + ClassExpression = 212, + OmittedExpression = 213, + ExpressionWithTypeArguments = 214, + AsExpression = 215, + NonNullExpression = 216, + MetaProperty = 217, + SyntheticExpression = 218, + TemplateSpan = 219, + SemicolonClassElement = 220, + Block = 221, + EmptyStatement = 222, + VariableStatement = 223, + ExpressionStatement = 224, + IfStatement = 225, + DoStatement = 226, + WhileStatement = 227, + ForStatement = 228, + ForInStatement = 229, + ForOfStatement = 230, + ContinueStatement = 231, + BreakStatement = 232, + ReturnStatement = 233, + WithStatement = 234, + SwitchStatement = 235, + LabeledStatement = 236, + ThrowStatement = 237, + TryStatement = 238, + DebuggerStatement = 239, + VariableDeclaration = 240, + VariableDeclarationList = 241, + FunctionDeclaration = 242, + ClassDeclaration = 243, + InterfaceDeclaration = 244, + TypeAliasDeclaration = 245, + EnumDeclaration = 246, + ModuleDeclaration = 247, + ModuleBlock = 248, + CaseBlock = 249, + NamespaceExportDeclaration = 250, + ImportEqualsDeclaration = 251, + ImportDeclaration = 252, + ImportClause = 253, + NamespaceImport = 254, + NamedImports = 255, + ImportSpecifier = 256, + ExportAssignment = 257, + ExportDeclaration = 258, + NamedExports = 259, + ExportSpecifier = 260, + MissingDeclaration = 261, + ExternalModuleReference = 262, + JsxElement = 263, + JsxSelfClosingElement = 264, + JsxOpeningElement = 265, + JsxClosingElement = 266, + JsxFragment = 267, + JsxOpeningFragment = 268, + JsxClosingFragment = 269, + JsxAttribute = 270, + JsxAttributes = 271, + JsxSpreadAttribute = 272, + JsxExpression = 273, + CaseClause = 274, + DefaultClause = 275, + HeritageClause = 276, + CatchClause = 277, + PropertyAssignment = 278, + ShorthandPropertyAssignment = 279, + SpreadAssignment = 280, + EnumMember = 281, + UnparsedPrologue = 282, + UnparsedPrepend = 283, + UnparsedText = 284, + UnparsedInternalText = 285, + UnparsedSyntheticReference = 286, + SourceFile = 287, + Bundle = 288, + UnparsedSource = 289, + InputFiles = 290, + JSDocTypeExpression = 291, + JSDocAllType = 292, + JSDocUnknownType = 293, + JSDocNullableType = 294, + JSDocNonNullableType = 295, + JSDocOptionalType = 296, + JSDocFunctionType = 297, + JSDocVariadicType = 298, + JSDocNamepathType = 299, + JSDocComment = 300, + JSDocTypeLiteral = 301, + JSDocSignature = 302, + JSDocTag = 303, + JSDocAugmentsTag = 304, + JSDocAuthorTag = 305, + JSDocClassTag = 306, + JSDocCallbackTag = 307, + JSDocEnumTag = 308, + JSDocParameterTag = 309, + JSDocReturnTag = 310, + JSDocThisTag = 311, + JSDocTypeTag = 312, + JSDocTemplateTag = 313, + JSDocTypedefTag = 314, + JSDocPropertyTag = 315, + SyntaxList = 316, + NotEmittedStatement = 317, + PartiallyEmittedExpression = 318, + CommaListExpression = 319, + MergeDeclarationMarker = 320, + EndOfDeclarationMarker = 321, + SyntheticReferenceExpression = 322, + Count = 323, + FirstAssignment = 61, + LastAssignment = 73, + FirstCompoundAssignment = 62, + LastCompoundAssignment = 73, + FirstReservedWord = 75, + LastReservedWord = 110, + FirstKeyword = 75, + LastKeyword = 150, + FirstFutureReservedWord = 111, + LastFutureReservedWord = 119, + FirstTypeNode = 166, + LastTypeNode = 186, FirstPunctuation = 18, - LastPunctuation = 72, + LastPunctuation = 73, FirstToken = 0, - LastToken = 149, + LastToken = 150, FirstTriviaToken = 2, LastTriviaToken = 7, FirstLiteralToken = 8, LastLiteralToken = 14, FirstTemplateToken = 14, LastTemplateToken = 17, - FirstBinaryOperator = 28, - LastBinaryOperator = 72, - FirstStatement = 222, - LastStatement = 238, - FirstNode = 150, - FirstJSDocNode = 290, - LastJSDocNode = 314, - FirstJSDocTagNode = 302, - LastJSDocTagNode = 314, + FirstBinaryOperator = 29, + LastBinaryOperator = 73, + FirstStatement = 223, + LastStatement = 239, + FirstNode = 151, + FirstJSDocNode = 291, + LastJSDocNode = 315, + FirstJSDocTagNode = 303, + LastJSDocTagNode = 315, } export enum NodeFlags { None = 0, @@ -438,27 +440,28 @@ declare namespace ts { NestedNamespace = 4, Synthesized = 8, Namespace = 16, - ExportContext = 32, - ContainsThis = 64, - HasImplicitReturn = 128, - HasExplicitReturn = 256, - GlobalAugmentation = 512, - HasAsyncFunctions = 1024, - DisallowInContext = 2048, - YieldContext = 4096, - DecoratorContext = 8192, - AwaitContext = 16384, - ThisNodeHasError = 32768, - JavaScriptFile = 65536, - ThisNodeOrAnySubNodesHasError = 131072, - HasAggregatedChildData = 262144, - JSDoc = 2097152, - JsonFile = 16777216, + OptionalChain = 32, + ExportContext = 64, + ContainsThis = 128, + HasImplicitReturn = 256, + HasExplicitReturn = 512, + GlobalAugmentation = 1024, + HasAsyncFunctions = 2048, + DisallowInContext = 4096, + YieldContext = 8192, + DecoratorContext = 16384, + AwaitContext = 32768, + ThisNodeHasError = 65536, + JavaScriptFile = 131072, + ThisNodeOrAnySubNodesHasError = 262144, + HasAggregatedChildData = 524288, + JSDoc = 4194304, + JsonFile = 33554432, BlockScoped = 3, - ReachabilityCheckFlags = 384, - ReachabilityAndEmitFlags = 1408, - ContextFlags = 12679168, - TypeExcludesFlags = 20480, + ReachabilityCheckFlags = 768, + ReachabilityAndEmitFlags = 2816, + ContextFlags = 25358336, + TypeExcludesFlags = 40960, } export enum ModifierFlags { None = 0, @@ -508,8 +511,10 @@ declare namespace ts { export interface Token extends Node { kind: TKind; } + export type DotToken = Token; export type DotDotDotToken = Token; export type QuestionToken = Token; + export type QuestionDotToken = Token; export type ExclamationToken = Token; export type ColonToken = Token; export type EqualsToken = Token; @@ -1082,8 +1087,12 @@ declare namespace ts { export interface PropertyAccessExpression extends MemberExpression, NamedDeclaration { kind: SyntaxKind.PropertyAccessExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; name: Identifier; } + export interface PropertyAccessChain extends PropertyAccessExpression { + _optionalChainBrand: any; + } export interface SuperPropertyAccessExpression extends PropertyAccessExpression { expression: SuperExpression; } @@ -1095,8 +1104,12 @@ declare namespace ts { export interface ElementAccessExpression extends MemberExpression { kind: SyntaxKind.ElementAccessExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; argumentExpression: Expression; } + export interface ElementAccessChain extends ElementAccessExpression { + _optionalChainBrand: any; + } export interface SuperElementAccessExpression extends ElementAccessExpression { expression: SuperExpression; } @@ -1104,9 +1117,14 @@ declare namespace ts { export interface CallExpression extends LeftHandSideExpression, Declaration { kind: SyntaxKind.CallExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; typeArguments?: NodeArray; arguments: NodeArray; } + export interface CallChain extends CallExpression { + _optionalChainBrand: any; + } + export type OptionalChain = PropertyAccessChain | ElementAccessChain | CallChain; export interface SuperCall extends CallExpression { expression: SuperExpression; } @@ -3496,8 +3514,12 @@ declare namespace ts { function isArrayLiteralExpression(node: Node): node is ArrayLiteralExpression; function isObjectLiteralExpression(node: Node): node is ObjectLiteralExpression; function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression; + function isPropertyAccessChain(node: Node): node is PropertyAccessChain; function isElementAccessExpression(node: Node): node is ElementAccessExpression; + function isElementAccessChain(node: Node): node is ElementAccessChain; function isCallExpression(node: Node): node is CallExpression; + function isCallChain(node: Node): node is CallChain; + function isOptionalChain(node: Node): node is PropertyAccessChain | ElementAccessChain | CallChain; function isNewExpression(node: Node): node is NewExpression; function isTaggedTemplateExpression(node: Node): node is TaggedTemplateExpression; function isTypeAssertion(node: Node): node is TypeAssertion; @@ -3943,10 +3965,16 @@ declare namespace ts { function updateObjectLiteral(node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]): ObjectLiteralExpression; function createPropertyAccess(expression: Expression, name: string | Identifier): PropertyAccessExpression; function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier): PropertyAccessExpression; + function createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier): PropertyAccessChain; + function updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier): PropertyAccessChain; function createElementAccess(expression: Expression, index: number | Expression): ElementAccessExpression; function updateElementAccess(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression): ElementAccessExpression; + function createElementAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, index: number | Expression): ElementAccessChain; + function updateElementAccessChain(node: ElementAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, argumentExpression: Expression): ElementAccessChain; function createCall(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined): CallExpression; function updateCall(node: CallExpression, expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]): CallExpression; + function createCallChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined): CallChain; + function updateCallChain(node: CallChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]): CallChain; function createNew(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined): NewExpression; function updateNew(node: NewExpression, expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined): NewExpression; /** @deprecated */ function createTaggedTemplate(tag: Expression, template: TemplateLiteral): TaggedTemplateExpression; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8ddf2033228a1..c159149433362 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -104,332 +104,334 @@ declare namespace ts { DotDotDotToken = 25, SemicolonToken = 26, CommaToken = 27, - LessThanToken = 28, - LessThanSlashToken = 29, - GreaterThanToken = 30, - LessThanEqualsToken = 31, - GreaterThanEqualsToken = 32, - EqualsEqualsToken = 33, - ExclamationEqualsToken = 34, - EqualsEqualsEqualsToken = 35, - ExclamationEqualsEqualsToken = 36, - EqualsGreaterThanToken = 37, - PlusToken = 38, - MinusToken = 39, - AsteriskToken = 40, - AsteriskAsteriskToken = 41, - SlashToken = 42, - PercentToken = 43, - PlusPlusToken = 44, - MinusMinusToken = 45, - LessThanLessThanToken = 46, - GreaterThanGreaterThanToken = 47, - GreaterThanGreaterThanGreaterThanToken = 48, - AmpersandToken = 49, - BarToken = 50, - CaretToken = 51, - ExclamationToken = 52, - TildeToken = 53, - AmpersandAmpersandToken = 54, - BarBarToken = 55, - QuestionToken = 56, - ColonToken = 57, - AtToken = 58, + QuestionDotToken = 28, + LessThanToken = 29, + LessThanSlashToken = 30, + GreaterThanToken = 31, + LessThanEqualsToken = 32, + GreaterThanEqualsToken = 33, + EqualsEqualsToken = 34, + ExclamationEqualsToken = 35, + EqualsEqualsEqualsToken = 36, + ExclamationEqualsEqualsToken = 37, + EqualsGreaterThanToken = 38, + PlusToken = 39, + MinusToken = 40, + AsteriskToken = 41, + AsteriskAsteriskToken = 42, + SlashToken = 43, + PercentToken = 44, + PlusPlusToken = 45, + MinusMinusToken = 46, + LessThanLessThanToken = 47, + GreaterThanGreaterThanToken = 48, + GreaterThanGreaterThanGreaterThanToken = 49, + AmpersandToken = 50, + BarToken = 51, + CaretToken = 52, + ExclamationToken = 53, + TildeToken = 54, + AmpersandAmpersandToken = 55, + BarBarToken = 56, + QuestionToken = 57, + ColonToken = 58, + AtToken = 59, /** Only the JSDoc scanner produces BacktickToken. The normal scanner produces NoSubstitutionTemplateLiteral and related kinds. */ - BacktickToken = 59, - EqualsToken = 60, - PlusEqualsToken = 61, - MinusEqualsToken = 62, - AsteriskEqualsToken = 63, - AsteriskAsteriskEqualsToken = 64, - SlashEqualsToken = 65, - PercentEqualsToken = 66, - LessThanLessThanEqualsToken = 67, - GreaterThanGreaterThanEqualsToken = 68, - GreaterThanGreaterThanGreaterThanEqualsToken = 69, - AmpersandEqualsToken = 70, - BarEqualsToken = 71, - CaretEqualsToken = 72, - Identifier = 73, - BreakKeyword = 74, - CaseKeyword = 75, - CatchKeyword = 76, - ClassKeyword = 77, - ConstKeyword = 78, - ContinueKeyword = 79, - DebuggerKeyword = 80, - DefaultKeyword = 81, - DeleteKeyword = 82, - DoKeyword = 83, - ElseKeyword = 84, - EnumKeyword = 85, - ExportKeyword = 86, - ExtendsKeyword = 87, - FalseKeyword = 88, - FinallyKeyword = 89, - ForKeyword = 90, - FunctionKeyword = 91, - IfKeyword = 92, - ImportKeyword = 93, - InKeyword = 94, - InstanceOfKeyword = 95, - NewKeyword = 96, - NullKeyword = 97, - ReturnKeyword = 98, - SuperKeyword = 99, - SwitchKeyword = 100, - ThisKeyword = 101, - ThrowKeyword = 102, - TrueKeyword = 103, - TryKeyword = 104, - TypeOfKeyword = 105, - VarKeyword = 106, - VoidKeyword = 107, - WhileKeyword = 108, - WithKeyword = 109, - ImplementsKeyword = 110, - InterfaceKeyword = 111, - LetKeyword = 112, - PackageKeyword = 113, - PrivateKeyword = 114, - ProtectedKeyword = 115, - PublicKeyword = 116, - StaticKeyword = 117, - YieldKeyword = 118, - AbstractKeyword = 119, - AsKeyword = 120, - AssertsKeyword = 121, - AnyKeyword = 122, - AsyncKeyword = 123, - AwaitKeyword = 124, - BooleanKeyword = 125, - ConstructorKeyword = 126, - DeclareKeyword = 127, - GetKeyword = 128, - InferKeyword = 129, - IsKeyword = 130, - KeyOfKeyword = 131, - ModuleKeyword = 132, - NamespaceKeyword = 133, - NeverKeyword = 134, - ReadonlyKeyword = 135, - RequireKeyword = 136, - NumberKeyword = 137, - ObjectKeyword = 138, - SetKeyword = 139, - StringKeyword = 140, - SymbolKeyword = 141, - TypeKeyword = 142, - UndefinedKeyword = 143, - UniqueKeyword = 144, - UnknownKeyword = 145, - FromKeyword = 146, - GlobalKeyword = 147, - BigIntKeyword = 148, - OfKeyword = 149, - QualifiedName = 150, - ComputedPropertyName = 151, - TypeParameter = 152, - Parameter = 153, - Decorator = 154, - PropertySignature = 155, - PropertyDeclaration = 156, - MethodSignature = 157, - MethodDeclaration = 158, - Constructor = 159, - GetAccessor = 160, - SetAccessor = 161, - CallSignature = 162, - ConstructSignature = 163, - IndexSignature = 164, - TypePredicate = 165, - TypeReference = 166, - FunctionType = 167, - ConstructorType = 168, - TypeQuery = 169, - TypeLiteral = 170, - ArrayType = 171, - TupleType = 172, - OptionalType = 173, - RestType = 174, - UnionType = 175, - IntersectionType = 176, - ConditionalType = 177, - InferType = 178, - ParenthesizedType = 179, - ThisType = 180, - TypeOperator = 181, - IndexedAccessType = 182, - MappedType = 183, - LiteralType = 184, - ImportType = 185, - ObjectBindingPattern = 186, - ArrayBindingPattern = 187, - BindingElement = 188, - ArrayLiteralExpression = 189, - ObjectLiteralExpression = 190, - PropertyAccessExpression = 191, - ElementAccessExpression = 192, - CallExpression = 193, - NewExpression = 194, - TaggedTemplateExpression = 195, - TypeAssertionExpression = 196, - ParenthesizedExpression = 197, - FunctionExpression = 198, - ArrowFunction = 199, - DeleteExpression = 200, - TypeOfExpression = 201, - VoidExpression = 202, - AwaitExpression = 203, - PrefixUnaryExpression = 204, - PostfixUnaryExpression = 205, - BinaryExpression = 206, - ConditionalExpression = 207, - TemplateExpression = 208, - YieldExpression = 209, - SpreadElement = 210, - ClassExpression = 211, - OmittedExpression = 212, - ExpressionWithTypeArguments = 213, - AsExpression = 214, - NonNullExpression = 215, - MetaProperty = 216, - SyntheticExpression = 217, - TemplateSpan = 218, - SemicolonClassElement = 219, - Block = 220, - EmptyStatement = 221, - VariableStatement = 222, - ExpressionStatement = 223, - IfStatement = 224, - DoStatement = 225, - WhileStatement = 226, - ForStatement = 227, - ForInStatement = 228, - ForOfStatement = 229, - ContinueStatement = 230, - BreakStatement = 231, - ReturnStatement = 232, - WithStatement = 233, - SwitchStatement = 234, - LabeledStatement = 235, - ThrowStatement = 236, - TryStatement = 237, - DebuggerStatement = 238, - VariableDeclaration = 239, - VariableDeclarationList = 240, - FunctionDeclaration = 241, - ClassDeclaration = 242, - InterfaceDeclaration = 243, - TypeAliasDeclaration = 244, - EnumDeclaration = 245, - ModuleDeclaration = 246, - ModuleBlock = 247, - CaseBlock = 248, - NamespaceExportDeclaration = 249, - ImportEqualsDeclaration = 250, - ImportDeclaration = 251, - ImportClause = 252, - NamespaceImport = 253, - NamedImports = 254, - ImportSpecifier = 255, - ExportAssignment = 256, - ExportDeclaration = 257, - NamedExports = 258, - ExportSpecifier = 259, - MissingDeclaration = 260, - ExternalModuleReference = 261, - JsxElement = 262, - JsxSelfClosingElement = 263, - JsxOpeningElement = 264, - JsxClosingElement = 265, - JsxFragment = 266, - JsxOpeningFragment = 267, - JsxClosingFragment = 268, - JsxAttribute = 269, - JsxAttributes = 270, - JsxSpreadAttribute = 271, - JsxExpression = 272, - CaseClause = 273, - DefaultClause = 274, - HeritageClause = 275, - CatchClause = 276, - PropertyAssignment = 277, - ShorthandPropertyAssignment = 278, - SpreadAssignment = 279, - EnumMember = 280, - UnparsedPrologue = 281, - UnparsedPrepend = 282, - UnparsedText = 283, - UnparsedInternalText = 284, - UnparsedSyntheticReference = 285, - SourceFile = 286, - Bundle = 287, - UnparsedSource = 288, - InputFiles = 289, - JSDocTypeExpression = 290, - JSDocAllType = 291, - JSDocUnknownType = 292, - JSDocNullableType = 293, - JSDocNonNullableType = 294, - JSDocOptionalType = 295, - JSDocFunctionType = 296, - JSDocVariadicType = 297, - JSDocNamepathType = 298, - JSDocComment = 299, - JSDocTypeLiteral = 300, - JSDocSignature = 301, - JSDocTag = 302, - JSDocAugmentsTag = 303, - JSDocAuthorTag = 304, - JSDocClassTag = 305, - JSDocCallbackTag = 306, - JSDocEnumTag = 307, - JSDocParameterTag = 308, - JSDocReturnTag = 309, - JSDocThisTag = 310, - JSDocTypeTag = 311, - JSDocTemplateTag = 312, - JSDocTypedefTag = 313, - JSDocPropertyTag = 314, - SyntaxList = 315, - NotEmittedStatement = 316, - PartiallyEmittedExpression = 317, - CommaListExpression = 318, - MergeDeclarationMarker = 319, - EndOfDeclarationMarker = 320, - Count = 321, - FirstAssignment = 60, - LastAssignment = 72, - FirstCompoundAssignment = 61, - LastCompoundAssignment = 72, - FirstReservedWord = 74, - LastReservedWord = 109, - FirstKeyword = 74, - LastKeyword = 149, - FirstFutureReservedWord = 110, - LastFutureReservedWord = 118, - FirstTypeNode = 165, - LastTypeNode = 185, + BacktickToken = 60, + EqualsToken = 61, + PlusEqualsToken = 62, + MinusEqualsToken = 63, + AsteriskEqualsToken = 64, + AsteriskAsteriskEqualsToken = 65, + SlashEqualsToken = 66, + PercentEqualsToken = 67, + LessThanLessThanEqualsToken = 68, + GreaterThanGreaterThanEqualsToken = 69, + GreaterThanGreaterThanGreaterThanEqualsToken = 70, + AmpersandEqualsToken = 71, + BarEqualsToken = 72, + CaretEqualsToken = 73, + Identifier = 74, + BreakKeyword = 75, + CaseKeyword = 76, + CatchKeyword = 77, + ClassKeyword = 78, + ConstKeyword = 79, + ContinueKeyword = 80, + DebuggerKeyword = 81, + DefaultKeyword = 82, + DeleteKeyword = 83, + DoKeyword = 84, + ElseKeyword = 85, + EnumKeyword = 86, + ExportKeyword = 87, + ExtendsKeyword = 88, + FalseKeyword = 89, + FinallyKeyword = 90, + ForKeyword = 91, + FunctionKeyword = 92, + IfKeyword = 93, + ImportKeyword = 94, + InKeyword = 95, + InstanceOfKeyword = 96, + NewKeyword = 97, + NullKeyword = 98, + ReturnKeyword = 99, + SuperKeyword = 100, + SwitchKeyword = 101, + ThisKeyword = 102, + ThrowKeyword = 103, + TrueKeyword = 104, + TryKeyword = 105, + TypeOfKeyword = 106, + VarKeyword = 107, + VoidKeyword = 108, + WhileKeyword = 109, + WithKeyword = 110, + ImplementsKeyword = 111, + InterfaceKeyword = 112, + LetKeyword = 113, + PackageKeyword = 114, + PrivateKeyword = 115, + ProtectedKeyword = 116, + PublicKeyword = 117, + StaticKeyword = 118, + YieldKeyword = 119, + AbstractKeyword = 120, + AsKeyword = 121, + AssertsKeyword = 122, + AnyKeyword = 123, + AsyncKeyword = 124, + AwaitKeyword = 125, + BooleanKeyword = 126, + ConstructorKeyword = 127, + DeclareKeyword = 128, + GetKeyword = 129, + InferKeyword = 130, + IsKeyword = 131, + KeyOfKeyword = 132, + ModuleKeyword = 133, + NamespaceKeyword = 134, + NeverKeyword = 135, + ReadonlyKeyword = 136, + RequireKeyword = 137, + NumberKeyword = 138, + ObjectKeyword = 139, + SetKeyword = 140, + StringKeyword = 141, + SymbolKeyword = 142, + TypeKeyword = 143, + UndefinedKeyword = 144, + UniqueKeyword = 145, + UnknownKeyword = 146, + FromKeyword = 147, + GlobalKeyword = 148, + BigIntKeyword = 149, + OfKeyword = 150, + QualifiedName = 151, + ComputedPropertyName = 152, + TypeParameter = 153, + Parameter = 154, + Decorator = 155, + PropertySignature = 156, + PropertyDeclaration = 157, + MethodSignature = 158, + MethodDeclaration = 159, + Constructor = 160, + GetAccessor = 161, + SetAccessor = 162, + CallSignature = 163, + ConstructSignature = 164, + IndexSignature = 165, + TypePredicate = 166, + TypeReference = 167, + FunctionType = 168, + ConstructorType = 169, + TypeQuery = 170, + TypeLiteral = 171, + ArrayType = 172, + TupleType = 173, + OptionalType = 174, + RestType = 175, + UnionType = 176, + IntersectionType = 177, + ConditionalType = 178, + InferType = 179, + ParenthesizedType = 180, + ThisType = 181, + TypeOperator = 182, + IndexedAccessType = 183, + MappedType = 184, + LiteralType = 185, + ImportType = 186, + ObjectBindingPattern = 187, + ArrayBindingPattern = 188, + BindingElement = 189, + ArrayLiteralExpression = 190, + ObjectLiteralExpression = 191, + PropertyAccessExpression = 192, + ElementAccessExpression = 193, + CallExpression = 194, + NewExpression = 195, + TaggedTemplateExpression = 196, + TypeAssertionExpression = 197, + ParenthesizedExpression = 198, + FunctionExpression = 199, + ArrowFunction = 200, + DeleteExpression = 201, + TypeOfExpression = 202, + VoidExpression = 203, + AwaitExpression = 204, + PrefixUnaryExpression = 205, + PostfixUnaryExpression = 206, + BinaryExpression = 207, + ConditionalExpression = 208, + TemplateExpression = 209, + YieldExpression = 210, + SpreadElement = 211, + ClassExpression = 212, + OmittedExpression = 213, + ExpressionWithTypeArguments = 214, + AsExpression = 215, + NonNullExpression = 216, + MetaProperty = 217, + SyntheticExpression = 218, + TemplateSpan = 219, + SemicolonClassElement = 220, + Block = 221, + EmptyStatement = 222, + VariableStatement = 223, + ExpressionStatement = 224, + IfStatement = 225, + DoStatement = 226, + WhileStatement = 227, + ForStatement = 228, + ForInStatement = 229, + ForOfStatement = 230, + ContinueStatement = 231, + BreakStatement = 232, + ReturnStatement = 233, + WithStatement = 234, + SwitchStatement = 235, + LabeledStatement = 236, + ThrowStatement = 237, + TryStatement = 238, + DebuggerStatement = 239, + VariableDeclaration = 240, + VariableDeclarationList = 241, + FunctionDeclaration = 242, + ClassDeclaration = 243, + InterfaceDeclaration = 244, + TypeAliasDeclaration = 245, + EnumDeclaration = 246, + ModuleDeclaration = 247, + ModuleBlock = 248, + CaseBlock = 249, + NamespaceExportDeclaration = 250, + ImportEqualsDeclaration = 251, + ImportDeclaration = 252, + ImportClause = 253, + NamespaceImport = 254, + NamedImports = 255, + ImportSpecifier = 256, + ExportAssignment = 257, + ExportDeclaration = 258, + NamedExports = 259, + ExportSpecifier = 260, + MissingDeclaration = 261, + ExternalModuleReference = 262, + JsxElement = 263, + JsxSelfClosingElement = 264, + JsxOpeningElement = 265, + JsxClosingElement = 266, + JsxFragment = 267, + JsxOpeningFragment = 268, + JsxClosingFragment = 269, + JsxAttribute = 270, + JsxAttributes = 271, + JsxSpreadAttribute = 272, + JsxExpression = 273, + CaseClause = 274, + DefaultClause = 275, + HeritageClause = 276, + CatchClause = 277, + PropertyAssignment = 278, + ShorthandPropertyAssignment = 279, + SpreadAssignment = 280, + EnumMember = 281, + UnparsedPrologue = 282, + UnparsedPrepend = 283, + UnparsedText = 284, + UnparsedInternalText = 285, + UnparsedSyntheticReference = 286, + SourceFile = 287, + Bundle = 288, + UnparsedSource = 289, + InputFiles = 290, + JSDocTypeExpression = 291, + JSDocAllType = 292, + JSDocUnknownType = 293, + JSDocNullableType = 294, + JSDocNonNullableType = 295, + JSDocOptionalType = 296, + JSDocFunctionType = 297, + JSDocVariadicType = 298, + JSDocNamepathType = 299, + JSDocComment = 300, + JSDocTypeLiteral = 301, + JSDocSignature = 302, + JSDocTag = 303, + JSDocAugmentsTag = 304, + JSDocAuthorTag = 305, + JSDocClassTag = 306, + JSDocCallbackTag = 307, + JSDocEnumTag = 308, + JSDocParameterTag = 309, + JSDocReturnTag = 310, + JSDocThisTag = 311, + JSDocTypeTag = 312, + JSDocTemplateTag = 313, + JSDocTypedefTag = 314, + JSDocPropertyTag = 315, + SyntaxList = 316, + NotEmittedStatement = 317, + PartiallyEmittedExpression = 318, + CommaListExpression = 319, + MergeDeclarationMarker = 320, + EndOfDeclarationMarker = 321, + SyntheticReferenceExpression = 322, + Count = 323, + FirstAssignment = 61, + LastAssignment = 73, + FirstCompoundAssignment = 62, + LastCompoundAssignment = 73, + FirstReservedWord = 75, + LastReservedWord = 110, + FirstKeyword = 75, + LastKeyword = 150, + FirstFutureReservedWord = 111, + LastFutureReservedWord = 119, + FirstTypeNode = 166, + LastTypeNode = 186, FirstPunctuation = 18, - LastPunctuation = 72, + LastPunctuation = 73, FirstToken = 0, - LastToken = 149, + LastToken = 150, FirstTriviaToken = 2, LastTriviaToken = 7, FirstLiteralToken = 8, LastLiteralToken = 14, FirstTemplateToken = 14, LastTemplateToken = 17, - FirstBinaryOperator = 28, - LastBinaryOperator = 72, - FirstStatement = 222, - LastStatement = 238, - FirstNode = 150, - FirstJSDocNode = 290, - LastJSDocNode = 314, - FirstJSDocTagNode = 302, - LastJSDocTagNode = 314, + FirstBinaryOperator = 29, + LastBinaryOperator = 73, + FirstStatement = 223, + LastStatement = 239, + FirstNode = 151, + FirstJSDocNode = 291, + LastJSDocNode = 315, + FirstJSDocTagNode = 303, + LastJSDocTagNode = 315, } export enum NodeFlags { None = 0, @@ -438,27 +440,28 @@ declare namespace ts { NestedNamespace = 4, Synthesized = 8, Namespace = 16, - ExportContext = 32, - ContainsThis = 64, - HasImplicitReturn = 128, - HasExplicitReturn = 256, - GlobalAugmentation = 512, - HasAsyncFunctions = 1024, - DisallowInContext = 2048, - YieldContext = 4096, - DecoratorContext = 8192, - AwaitContext = 16384, - ThisNodeHasError = 32768, - JavaScriptFile = 65536, - ThisNodeOrAnySubNodesHasError = 131072, - HasAggregatedChildData = 262144, - JSDoc = 2097152, - JsonFile = 16777216, + OptionalChain = 32, + ExportContext = 64, + ContainsThis = 128, + HasImplicitReturn = 256, + HasExplicitReturn = 512, + GlobalAugmentation = 1024, + HasAsyncFunctions = 2048, + DisallowInContext = 4096, + YieldContext = 8192, + DecoratorContext = 16384, + AwaitContext = 32768, + ThisNodeHasError = 65536, + JavaScriptFile = 131072, + ThisNodeOrAnySubNodesHasError = 262144, + HasAggregatedChildData = 524288, + JSDoc = 4194304, + JsonFile = 33554432, BlockScoped = 3, - ReachabilityCheckFlags = 384, - ReachabilityAndEmitFlags = 1408, - ContextFlags = 12679168, - TypeExcludesFlags = 20480, + ReachabilityCheckFlags = 768, + ReachabilityAndEmitFlags = 2816, + ContextFlags = 25358336, + TypeExcludesFlags = 40960, } export enum ModifierFlags { None = 0, @@ -508,8 +511,10 @@ declare namespace ts { export interface Token extends Node { kind: TKind; } + export type DotToken = Token; export type DotDotDotToken = Token; export type QuestionToken = Token; + export type QuestionDotToken = Token; export type ExclamationToken = Token; export type ColonToken = Token; export type EqualsToken = Token; @@ -1082,8 +1087,12 @@ declare namespace ts { export interface PropertyAccessExpression extends MemberExpression, NamedDeclaration { kind: SyntaxKind.PropertyAccessExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; name: Identifier; } + export interface PropertyAccessChain extends PropertyAccessExpression { + _optionalChainBrand: any; + } export interface SuperPropertyAccessExpression extends PropertyAccessExpression { expression: SuperExpression; } @@ -1095,8 +1104,12 @@ declare namespace ts { export interface ElementAccessExpression extends MemberExpression { kind: SyntaxKind.ElementAccessExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; argumentExpression: Expression; } + export interface ElementAccessChain extends ElementAccessExpression { + _optionalChainBrand: any; + } export interface SuperElementAccessExpression extends ElementAccessExpression { expression: SuperExpression; } @@ -1104,9 +1117,14 @@ declare namespace ts { export interface CallExpression extends LeftHandSideExpression, Declaration { kind: SyntaxKind.CallExpression; expression: LeftHandSideExpression; + questionDotToken?: QuestionDotToken; typeArguments?: NodeArray; arguments: NodeArray; } + export interface CallChain extends CallExpression { + _optionalChainBrand: any; + } + export type OptionalChain = PropertyAccessChain | ElementAccessChain | CallChain; export interface SuperCall extends CallExpression { expression: SuperExpression; } @@ -3496,8 +3514,12 @@ declare namespace ts { function isArrayLiteralExpression(node: Node): node is ArrayLiteralExpression; function isObjectLiteralExpression(node: Node): node is ObjectLiteralExpression; function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression; + function isPropertyAccessChain(node: Node): node is PropertyAccessChain; function isElementAccessExpression(node: Node): node is ElementAccessExpression; + function isElementAccessChain(node: Node): node is ElementAccessChain; function isCallExpression(node: Node): node is CallExpression; + function isCallChain(node: Node): node is CallChain; + function isOptionalChain(node: Node): node is PropertyAccessChain | ElementAccessChain | CallChain; function isNewExpression(node: Node): node is NewExpression; function isTaggedTemplateExpression(node: Node): node is TaggedTemplateExpression; function isTypeAssertion(node: Node): node is TypeAssertion; @@ -3943,10 +3965,16 @@ declare namespace ts { function updateObjectLiteral(node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]): ObjectLiteralExpression; function createPropertyAccess(expression: Expression, name: string | Identifier): PropertyAccessExpression; function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier): PropertyAccessExpression; + function createPropertyAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: string | Identifier): PropertyAccessChain; + function updatePropertyAccessChain(node: PropertyAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: Identifier): PropertyAccessChain; function createElementAccess(expression: Expression, index: number | Expression): ElementAccessExpression; function updateElementAccess(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression): ElementAccessExpression; + function createElementAccessChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, index: number | Expression): ElementAccessChain; + function updateElementAccessChain(node: ElementAccessChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, argumentExpression: Expression): ElementAccessChain; function createCall(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined): CallExpression; function updateCall(node: CallExpression, expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]): CallExpression; + function createCallChain(expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined): CallChain; + function updateCallChain(node: CallChain, expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[]): CallChain; function createNew(expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined): NewExpression; function updateNew(node: NewExpression, expression: Expression, typeArguments: readonly TypeNode[] | undefined, argumentsArray: readonly Expression[] | undefined): NewExpression; /** @deprecated */ function createTaggedTemplate(tag: Expression, template: TemplateLiteral): TaggedTemplateExpression; diff --git a/tests/baselines/reference/callChain.2.js b/tests/baselines/reference/callChain.2.js new file mode 100644 index 0000000000000..a44115ef91673 --- /dev/null +++ b/tests/baselines/reference/callChain.2.js @@ -0,0 +1,16 @@ +//// [callChain.2.ts] +declare const o1: undefined | (() => number); +o1?.(); + +declare const o2: undefined | { b: () => number }; +o2?.b(); + +declare const o3: { b: (() => { c: string }) | undefined }; +o3.b?.().c; + + +//// [callChain.2.js] +var _a, _b, _c, _d; +(_a = o1) === null || _a === void 0 ? void 0 : _a(); +(_b = o2) === null || _b === void 0 ? void 0 : _b.b(); +(_d = (_c = o3).b) === null || _d === void 0 ? void 0 : _d.call(_c).c; diff --git a/tests/baselines/reference/callChain.2.symbols b/tests/baselines/reference/callChain.2.symbols new file mode 100644 index 0000000000000..26c8859940e74 --- /dev/null +++ b/tests/baselines/reference/callChain.2.symbols @@ -0,0 +1,28 @@ +=== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts === +declare const o1: undefined | (() => number); +>o1 : Symbol(o1, Decl(callChain.2.ts, 0, 13)) + +o1?.(); +>o1 : Symbol(o1, Decl(callChain.2.ts, 0, 13)) + +declare const o2: undefined | { b: () => number }; +>o2 : Symbol(o2, Decl(callChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(callChain.2.ts, 3, 31)) + +o2?.b(); +>o2?.b : Symbol(b, Decl(callChain.2.ts, 3, 31)) +>o2 : Symbol(o2, Decl(callChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(callChain.2.ts, 3, 31)) + +declare const o3: { b: (() => { c: string }) | undefined }; +>o3 : Symbol(o3, Decl(callChain.2.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.2.ts, 6, 19)) +>c : Symbol(c, Decl(callChain.2.ts, 6, 31)) + +o3.b?.().c; +>o3.b?.().c : Symbol(c, Decl(callChain.2.ts, 6, 31)) +>o3.b : Symbol(b, Decl(callChain.2.ts, 6, 19)) +>o3 : Symbol(o3, Decl(callChain.2.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.2.ts, 6, 19)) +>c : Symbol(c, Decl(callChain.2.ts, 6, 31)) + diff --git a/tests/baselines/reference/callChain.2.types b/tests/baselines/reference/callChain.2.types new file mode 100644 index 0000000000000..53ea569cf8e59 --- /dev/null +++ b/tests/baselines/reference/callChain.2.types @@ -0,0 +1,31 @@ +=== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts === +declare const o1: undefined | (() => number); +>o1 : () => number + +o1?.(); +>o1?.() : number +>o1 : () => number + +declare const o2: undefined | { b: () => number }; +>o2 : { b: () => number; } +>b : () => number + +o2?.b(); +>o2?.b() : number +>o2?.b : () => number +>o2 : { b: () => number; } +>b : () => number + +declare const o3: { b: (() => { c: string }) | undefined }; +>o3 : { b: () => { c: string; }; } +>b : () => { c: string; } +>c : string + +o3.b?.().c; +>o3.b?.().c : string +>o3.b?.() : { c: string; } +>o3.b : () => { c: string; } +>o3 : { b: () => { c: string; }; } +>b : () => { c: string; } +>c : string + diff --git a/tests/baselines/reference/callChain.3.errors.txt b/tests/baselines/reference/callChain.3.errors.txt new file mode 100644 index 0000000000000..6deb1db1b4bef --- /dev/null +++ b/tests/baselines/reference/callChain.3.errors.txt @@ -0,0 +1,23 @@ +tests/cases/conformance/expressions/optionalChaining/callChain/callChain.3.ts(3,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'. + Type 'undefined' is not assignable to type 'number'. +tests/cases/conformance/expressions/optionalChaining/callChain/callChain.3.ts(4,7): error TS2322: Type 'number | undefined' is not assignable to type 'number'. + Type 'undefined' is not assignable to type 'number'. + + +==== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.3.ts (2 errors) ==== + declare function absorb(): T; + declare const a: { m?(obj: {x: T}): T } | undefined; + const n1: number = a?.m?.({x: 12 }); // should be an error (`undefined` is not assignable to `number`) + ~~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'number'. +!!! error TS2322: Type 'undefined' is not assignable to type 'number'. + const n2: number = a?.m?.({x: absorb()}); // likewise + ~~ +!!! error TS2322: Type 'number | undefined' is not assignable to type 'number'. +!!! error TS2322: Type 'undefined' is not assignable to type 'number'. + const n3: number | undefined = a?.m?.({x: 12}); // should be ok + const n4: number | undefined = a?.m?.({x: absorb()}); // likewise + + // Also a test showing `!` vs `?` for good measure + let t1 = a?.m?.({x: 12}); + t1 = a!.m!({x: 12}); \ No newline at end of file diff --git a/tests/baselines/reference/callChain.3.js b/tests/baselines/reference/callChain.3.js new file mode 100644 index 0000000000000..2a6685d0d53f5 --- /dev/null +++ b/tests/baselines/reference/callChain.3.js @@ -0,0 +1,22 @@ +//// [callChain.3.ts] +declare function absorb(): T; +declare const a: { m?(obj: {x: T}): T } | undefined; +const n1: number = a?.m?.({x: 12 }); // should be an error (`undefined` is not assignable to `number`) +const n2: number = a?.m?.({x: absorb()}); // likewise +const n3: number | undefined = a?.m?.({x: 12}); // should be ok +const n4: number | undefined = a?.m?.({x: absorb()}); // likewise + +// Also a test showing `!` vs `?` for good measure +let t1 = a?.m?.({x: 12}); +t1 = a!.m!({x: 12}); + +//// [callChain.3.js] +"use strict"; +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q; +var n1 = (_c = (_a = a) === null || _a === void 0 ? void 0 : (_b = _a).m) === null || _c === void 0 ? void 0 : _c.call(_b, { x: 12 }); // should be an error (`undefined` is not assignable to `number`) +var n2 = (_f = (_d = a) === null || _d === void 0 ? void 0 : (_e = _d).m) === null || _f === void 0 ? void 0 : _f.call(_e, { x: absorb() }); // likewise +var n3 = (_j = (_g = a) === null || _g === void 0 ? void 0 : (_h = _g).m) === null || _j === void 0 ? void 0 : _j.call(_h, { x: 12 }); // should be ok +var n4 = (_m = (_k = a) === null || _k === void 0 ? void 0 : (_l = _k).m) === null || _m === void 0 ? void 0 : _m.call(_l, { x: absorb() }); // likewise +// Also a test showing `!` vs `?` for good measure +var t1 = (_q = (_o = a) === null || _o === void 0 ? void 0 : (_p = _o).m) === null || _q === void 0 ? void 0 : _q.call(_p, { x: 12 }); +t1 = a.m({ x: 12 }); diff --git a/tests/baselines/reference/callChain.3.symbols b/tests/baselines/reference/callChain.3.symbols new file mode 100644 index 0000000000000..eb5a6ae9df2f0 --- /dev/null +++ b/tests/baselines/reference/callChain.3.symbols @@ -0,0 +1,60 @@ +=== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.3.ts === +declare function absorb(): T; +>absorb : Symbol(absorb, Decl(callChain.3.ts, 0, 0)) +>T : Symbol(T, Decl(callChain.3.ts, 0, 24)) +>T : Symbol(T, Decl(callChain.3.ts, 0, 24)) + +declare const a: { m?(obj: {x: T}): T } | undefined; +>a : Symbol(a, Decl(callChain.3.ts, 1, 13)) +>m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>T : Symbol(T, Decl(callChain.3.ts, 1, 22)) +>obj : Symbol(obj, Decl(callChain.3.ts, 1, 25)) +>x : Symbol(x, Decl(callChain.3.ts, 1, 31)) +>T : Symbol(T, Decl(callChain.3.ts, 1, 22)) +>T : Symbol(T, Decl(callChain.3.ts, 1, 22)) + +const n1: number = a?.m?.({x: 12 }); // should be an error (`undefined` is not assignable to `number`) +>n1 : Symbol(n1, Decl(callChain.3.ts, 2, 5)) +>a?.m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>a : Symbol(a, Decl(callChain.3.ts, 1, 13)) +>m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>x : Symbol(x, Decl(callChain.3.ts, 2, 27)) + +const n2: number = a?.m?.({x: absorb()}); // likewise +>n2 : Symbol(n2, Decl(callChain.3.ts, 3, 5)) +>a?.m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>a : Symbol(a, Decl(callChain.3.ts, 1, 13)) +>m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>x : Symbol(x, Decl(callChain.3.ts, 3, 27)) +>absorb : Symbol(absorb, Decl(callChain.3.ts, 0, 0)) + +const n3: number | undefined = a?.m?.({x: 12}); // should be ok +>n3 : Symbol(n3, Decl(callChain.3.ts, 4, 5)) +>a?.m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>a : Symbol(a, Decl(callChain.3.ts, 1, 13)) +>m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>x : Symbol(x, Decl(callChain.3.ts, 4, 39)) + +const n4: number | undefined = a?.m?.({x: absorb()}); // likewise +>n4 : Symbol(n4, Decl(callChain.3.ts, 5, 5)) +>a?.m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>a : Symbol(a, Decl(callChain.3.ts, 1, 13)) +>m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>x : Symbol(x, Decl(callChain.3.ts, 5, 39)) +>absorb : Symbol(absorb, Decl(callChain.3.ts, 0, 0)) + +// Also a test showing `!` vs `?` for good measure +let t1 = a?.m?.({x: 12}); +>t1 : Symbol(t1, Decl(callChain.3.ts, 8, 3)) +>a?.m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>a : Symbol(a, Decl(callChain.3.ts, 1, 13)) +>m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>x : Symbol(x, Decl(callChain.3.ts, 8, 17)) + +t1 = a!.m!({x: 12}); +>t1 : Symbol(t1, Decl(callChain.3.ts, 8, 3)) +>a!.m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>a : Symbol(a, Decl(callChain.3.ts, 1, 13)) +>m : Symbol(m, Decl(callChain.3.ts, 1, 18)) +>x : Symbol(x, Decl(callChain.3.ts, 9, 12)) + diff --git a/tests/baselines/reference/callChain.3.types b/tests/baselines/reference/callChain.3.types new file mode 100644 index 0000000000000..7646b5418a7ee --- /dev/null +++ b/tests/baselines/reference/callChain.3.types @@ -0,0 +1,76 @@ +=== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.3.ts === +declare function absorb(): T; +>absorb : () => T + +declare const a: { m?(obj: {x: T}): T } | undefined; +>a : { m?(obj: { x: T; }): T; } | undefined +>m : ((obj: { x: T; }) => T) | undefined +>obj : { x: T; } +>x : T + +const n1: number = a?.m?.({x: 12 }); // should be an error (`undefined` is not assignable to `number`) +>n1 : number +>a?.m?.({x: 12 }) : number | undefined +>a?.m : ((obj: { x: T; }) => T) | undefined +>a : { m?(obj: { x: T; }): T; } | undefined +>m : ((obj: { x: T; }) => T) | undefined +>{x: 12 } : { x: number; } +>x : number +>12 : 12 + +const n2: number = a?.m?.({x: absorb()}); // likewise +>n2 : number +>a?.m?.({x: absorb()}) : number | undefined +>a?.m : ((obj: { x: T; }) => T) | undefined +>a : { m?(obj: { x: T; }): T; } | undefined +>m : ((obj: { x: T; }) => T) | undefined +>{x: absorb()} : { x: number; } +>x : number +>absorb() : number +>absorb : () => T + +const n3: number | undefined = a?.m?.({x: 12}); // should be ok +>n3 : number | undefined +>a?.m?.({x: 12}) : number | undefined +>a?.m : ((obj: { x: T; }) => T) | undefined +>a : { m?(obj: { x: T; }): T; } | undefined +>m : ((obj: { x: T; }) => T) | undefined +>{x: 12} : { x: number; } +>x : number +>12 : 12 + +const n4: number | undefined = a?.m?.({x: absorb()}); // likewise +>n4 : number | undefined +>a?.m?.({x: absorb()}) : number | undefined +>a?.m : ((obj: { x: T; }) => T) | undefined +>a : { m?(obj: { x: T; }): T; } | undefined +>m : ((obj: { x: T; }) => T) | undefined +>{x: absorb()} : { x: number | undefined; } +>x : number | undefined +>absorb() : number | undefined +>absorb : () => T + +// Also a test showing `!` vs `?` for good measure +let t1 = a?.m?.({x: 12}); +>t1 : number | undefined +>a?.m?.({x: 12}) : number | undefined +>a?.m : ((obj: { x: T; }) => T) | undefined +>a : { m?(obj: { x: T; }): T; } | undefined +>m : ((obj: { x: T; }) => T) | undefined +>{x: 12} : { x: number; } +>x : number +>12 : 12 + +t1 = a!.m!({x: 12}); +>t1 = a!.m!({x: 12}) : number +>t1 : number | undefined +>a!.m!({x: 12}) : number +>a!.m! : (obj: { x: T; }) => T +>a!.m : ((obj: { x: T; }) => T) | undefined +>a! : { m?(obj: { x: T; }): T; } +>a : { m?(obj: { x: T; }): T; } | undefined +>m : ((obj: { x: T; }) => T) | undefined +>{x: 12} : { x: number; } +>x : number +>12 : 12 + diff --git a/tests/baselines/reference/callChain.js b/tests/baselines/reference/callChain.js new file mode 100644 index 0000000000000..1ef797b5ad1b3 --- /dev/null +++ b/tests/baselines/reference/callChain.js @@ -0,0 +1,70 @@ +//// [callChain.ts] +declare const o1: undefined | ((...args: any[]) => number); +o1?.(); +o1?.(1); +o1?.(...[1, 2]); +o1?.(1, ...[2, 3], 4); + +declare const o2: undefined | { b: (...args: any[]) => number }; +o2?.b(); +o2?.b(1); +o2?.b(...[1, 2]); +o2?.b(1, ...[2, 3], 4); +o2?.["b"](); +o2?.["b"](1); +o2?.["b"](...[1, 2]); +o2?.["b"](1, ...[2, 3], 4); + +declare const o3: { b: ((...args: any[]) => { c: string }) | undefined }; +o3.b?.().c; +o3.b?.(1).c; +o3.b?.(...[1, 2]).c; +o3.b?.(1, ...[2, 3], 4).c; +o3.b?.()["c"]; +o3.b?.(1)["c"]; +o3.b?.(...[1, 2])["c"]; +o3.b?.(1, ...[2, 3], 4)["c"]; +o3["b"]?.().c; +o3["b"]?.(1).c; +o3["b"]?.(...[1, 2]).c; +o3["b"]?.(1, ...[2, 3], 4).c; + +declare const o4: undefined | ((f: (a: T) => T) => T); +declare function incr(x: number): number; +const v: number | undefined = o4?.(incr); + +//// [callChain.js] +"use strict"; +var __spreadArrays = (this && this.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12; +(_a = o1) === null || _a === void 0 ? void 0 : _a(); +(_b = o1) === null || _b === void 0 ? void 0 : _b(1); +(_c = o1) === null || _c === void 0 ? void 0 : _c.apply(void 0, [1, 2]); +(_d = o1) === null || _d === void 0 ? void 0 : _d.apply(void 0, __spreadArrays([1], [2, 3], [4])); +(_e = o2) === null || _e === void 0 ? void 0 : _e.b(); +(_f = o2) === null || _f === void 0 ? void 0 : _f.b(1); +(_g = o2) === null || _g === void 0 ? void 0 : _g.b.apply(_g, [1, 2]); +(_h = o2) === null || _h === void 0 ? void 0 : _h.b.apply(_h, __spreadArrays([1], [2, 3], [4])); +(_j = o2) === null || _j === void 0 ? void 0 : _j["b"](); +(_k = o2) === null || _k === void 0 ? void 0 : _k["b"](1); +(_l = o2) === null || _l === void 0 ? void 0 : _l["b"].apply(_l, [1, 2]); +(_m = o2) === null || _m === void 0 ? void 0 : _m["b"].apply(_m, __spreadArrays([1], [2, 3], [4])); +(_p = (_o = o3).b) === null || _p === void 0 ? void 0 : _p.call(_o).c; +(_r = (_q = o3).b) === null || _r === void 0 ? void 0 : _r.call(_q, 1).c; +(_t = (_s = o3).b) === null || _t === void 0 ? void 0 : _t.call.apply(_t, __spreadArrays([_s], [1, 2])).c; +(_v = (_u = o3).b) === null || _v === void 0 ? void 0 : _v.call.apply(_v, __spreadArrays([_u, 1], [2, 3], [4])).c; +(_x = (_w = o3).b) === null || _x === void 0 ? void 0 : _x.call(_w)["c"]; +(_z = (_y = o3).b) === null || _z === void 0 ? void 0 : _z.call(_y, 1)["c"]; +(_1 = (_0 = o3).b) === null || _1 === void 0 ? void 0 : _1.call.apply(_1, __spreadArrays([_0], [1, 2]))["c"]; +(_3 = (_2 = o3).b) === null || _3 === void 0 ? void 0 : _3.call.apply(_3, __spreadArrays([_2, 1], [2, 3], [4]))["c"]; +(_5 = (_4 = o3)["b"]) === null || _5 === void 0 ? void 0 : _5.call(_4).c; +(_7 = (_6 = o3)["b"]) === null || _7 === void 0 ? void 0 : _7.call(_6, 1).c; +(_9 = (_8 = o3)["b"]) === null || _9 === void 0 ? void 0 : _9.call.apply(_9, __spreadArrays([_8], [1, 2])).c; +(_11 = (_10 = o3)["b"]) === null || _11 === void 0 ? void 0 : _11.call.apply(_11, __spreadArrays([_10, 1], [2, 3], [4])).c; +var v = (_12 = o4) === null || _12 === void 0 ? void 0 : _12(incr); diff --git a/tests/baselines/reference/callChain.symbols b/tests/baselines/reference/callChain.symbols new file mode 100644 index 0000000000000..dd52388bda60f --- /dev/null +++ b/tests/baselines/reference/callChain.symbols @@ -0,0 +1,150 @@ +=== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.ts === +declare const o1: undefined | ((...args: any[]) => number); +>o1 : Symbol(o1, Decl(callChain.ts, 0, 13)) +>args : Symbol(args, Decl(callChain.ts, 0, 32)) + +o1?.(); +>o1 : Symbol(o1, Decl(callChain.ts, 0, 13)) + +o1?.(1); +>o1 : Symbol(o1, Decl(callChain.ts, 0, 13)) + +o1?.(...[1, 2]); +>o1 : Symbol(o1, Decl(callChain.ts, 0, 13)) + +o1?.(1, ...[2, 3], 4); +>o1 : Symbol(o1, Decl(callChain.ts, 0, 13)) + +declare const o2: undefined | { b: (...args: any[]) => number }; +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.ts, 6, 31)) +>args : Symbol(args, Decl(callChain.ts, 6, 36)) + +o2?.b(); +>o2?.b : Symbol(b, Decl(callChain.ts, 6, 31)) +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.ts, 6, 31)) + +o2?.b(1); +>o2?.b : Symbol(b, Decl(callChain.ts, 6, 31)) +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.ts, 6, 31)) + +o2?.b(...[1, 2]); +>o2?.b : Symbol(b, Decl(callChain.ts, 6, 31)) +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.ts, 6, 31)) + +o2?.b(1, ...[2, 3], 4); +>o2?.b : Symbol(b, Decl(callChain.ts, 6, 31)) +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) +>b : Symbol(b, Decl(callChain.ts, 6, 31)) + +o2?.["b"](); +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) + +o2?.["b"](1); +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) + +o2?.["b"](...[1, 2]); +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) + +o2?.["b"](1, ...[2, 3], 4); +>o2 : Symbol(o2, Decl(callChain.ts, 6, 13)) + +declare const o3: { b: ((...args: any[]) => { c: string }) | undefined }; +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) +>args : Symbol(args, Decl(callChain.ts, 16, 25)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +o3.b?.().c; +>o3.b?.().c : Symbol(c, Decl(callChain.ts, 16, 45)) +>o3.b : Symbol(b, Decl(callChain.ts, 16, 19)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +o3.b?.(1).c; +>o3.b?.(1).c : Symbol(c, Decl(callChain.ts, 16, 45)) +>o3.b : Symbol(b, Decl(callChain.ts, 16, 19)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +o3.b?.(...[1, 2]).c; +>o3.b?.(...[1, 2]).c : Symbol(c, Decl(callChain.ts, 16, 45)) +>o3.b : Symbol(b, Decl(callChain.ts, 16, 19)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +o3.b?.(1, ...[2, 3], 4).c; +>o3.b?.(1, ...[2, 3], 4).c : Symbol(c, Decl(callChain.ts, 16, 45)) +>o3.b : Symbol(b, Decl(callChain.ts, 16, 19)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +o3.b?.()["c"]; +>o3.b : Symbol(b, Decl(callChain.ts, 16, 19)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) + +o3.b?.(1)["c"]; +>o3.b : Symbol(b, Decl(callChain.ts, 16, 19)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) + +o3.b?.(...[1, 2])["c"]; +>o3.b : Symbol(b, Decl(callChain.ts, 16, 19)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) + +o3.b?.(1, ...[2, 3], 4)["c"]; +>o3.b : Symbol(b, Decl(callChain.ts, 16, 19)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>b : Symbol(b, Decl(callChain.ts, 16, 19)) + +o3["b"]?.().c; +>o3["b"]?.().c : Symbol(c, Decl(callChain.ts, 16, 45)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>"b" : Symbol(b, Decl(callChain.ts, 16, 19)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +o3["b"]?.(1).c; +>o3["b"]?.(1).c : Symbol(c, Decl(callChain.ts, 16, 45)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>"b" : Symbol(b, Decl(callChain.ts, 16, 19)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +o3["b"]?.(...[1, 2]).c; +>o3["b"]?.(...[1, 2]).c : Symbol(c, Decl(callChain.ts, 16, 45)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>"b" : Symbol(b, Decl(callChain.ts, 16, 19)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +o3["b"]?.(1, ...[2, 3], 4).c; +>o3["b"]?.(1, ...[2, 3], 4).c : Symbol(c, Decl(callChain.ts, 16, 45)) +>o3 : Symbol(o3, Decl(callChain.ts, 16, 13)) +>"b" : Symbol(b, Decl(callChain.ts, 16, 19)) +>c : Symbol(c, Decl(callChain.ts, 16, 45)) + +declare const o4: undefined | ((f: (a: T) => T) => T); +>o4 : Symbol(o4, Decl(callChain.ts, 30, 13)) +>T : Symbol(T, Decl(callChain.ts, 30, 32)) +>f : Symbol(f, Decl(callChain.ts, 30, 35)) +>a : Symbol(a, Decl(callChain.ts, 30, 39)) +>T : Symbol(T, Decl(callChain.ts, 30, 32)) +>T : Symbol(T, Decl(callChain.ts, 30, 32)) +>T : Symbol(T, Decl(callChain.ts, 30, 32)) + +declare function incr(x: number): number; +>incr : Symbol(incr, Decl(callChain.ts, 30, 57)) +>x : Symbol(x, Decl(callChain.ts, 31, 22)) + +const v: number | undefined = o4?.(incr); +>v : Symbol(v, Decl(callChain.ts, 32, 5)) +>o4 : Symbol(o4, Decl(callChain.ts, 30, 13)) +>incr : Symbol(incr, Decl(callChain.ts, 30, 57)) + diff --git a/tests/baselines/reference/callChain.types b/tests/baselines/reference/callChain.types new file mode 100644 index 0000000000000..70f835bf568c4 --- /dev/null +++ b/tests/baselines/reference/callChain.types @@ -0,0 +1,257 @@ +=== tests/cases/conformance/expressions/optionalChaining/callChain/callChain.ts === +declare const o1: undefined | ((...args: any[]) => number); +>o1 : ((...args: any[]) => number) | undefined +>args : any[] + +o1?.(); +>o1?.() : number | undefined +>o1 : ((...args: any[]) => number) | undefined + +o1?.(1); +>o1?.(1) : number | undefined +>o1 : ((...args: any[]) => number) | undefined +>1 : 1 + +o1?.(...[1, 2]); +>o1?.(...[1, 2]) : number | undefined +>o1 : ((...args: any[]) => number) | undefined +>...[1, 2] : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 + +o1?.(1, ...[2, 3], 4); +>o1?.(1, ...[2, 3], 4) : number | undefined +>o1 : ((...args: any[]) => number) | undefined +>1 : 1 +>...[2, 3] : number +>[2, 3] : number[] +>2 : 2 +>3 : 3 +>4 : 4 + +declare const o2: undefined | { b: (...args: any[]) => number }; +>o2 : { b: (...args: any[]) => number; } | undefined +>b : (...args: any[]) => number +>args : any[] + +o2?.b(); +>o2?.b() : number | undefined +>o2?.b : ((...args: any[]) => number) | undefined +>o2 : { b: (...args: any[]) => number; } | undefined +>b : ((...args: any[]) => number) | undefined + +o2?.b(1); +>o2?.b(1) : number | undefined +>o2?.b : ((...args: any[]) => number) | undefined +>o2 : { b: (...args: any[]) => number; } | undefined +>b : ((...args: any[]) => number) | undefined +>1 : 1 + +o2?.b(...[1, 2]); +>o2?.b(...[1, 2]) : number | undefined +>o2?.b : ((...args: any[]) => number) | undefined +>o2 : { b: (...args: any[]) => number; } | undefined +>b : ((...args: any[]) => number) | undefined +>...[1, 2] : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 + +o2?.b(1, ...[2, 3], 4); +>o2?.b(1, ...[2, 3], 4) : number | undefined +>o2?.b : ((...args: any[]) => number) | undefined +>o2 : { b: (...args: any[]) => number; } | undefined +>b : ((...args: any[]) => number) | undefined +>1 : 1 +>...[2, 3] : number +>[2, 3] : number[] +>2 : 2 +>3 : 3 +>4 : 4 + +o2?.["b"](); +>o2?.["b"]() : number | undefined +>o2?.["b"] : ((...args: any[]) => number) | undefined +>o2 : { b: (...args: any[]) => number; } | undefined +>"b" : "b" + +o2?.["b"](1); +>o2?.["b"](1) : number | undefined +>o2?.["b"] : ((...args: any[]) => number) | undefined +>o2 : { b: (...args: any[]) => number; } | undefined +>"b" : "b" +>1 : 1 + +o2?.["b"](...[1, 2]); +>o2?.["b"](...[1, 2]) : number | undefined +>o2?.["b"] : ((...args: any[]) => number) | undefined +>o2 : { b: (...args: any[]) => number; } | undefined +>"b" : "b" +>...[1, 2] : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 + +o2?.["b"](1, ...[2, 3], 4); +>o2?.["b"](1, ...[2, 3], 4) : number | undefined +>o2?.["b"] : ((...args: any[]) => number) | undefined +>o2 : { b: (...args: any[]) => number; } | undefined +>"b" : "b" +>1 : 1 +>...[2, 3] : number +>[2, 3] : number[] +>2 : 2 +>3 : 3 +>4 : 4 + +declare const o3: { b: ((...args: any[]) => { c: string }) | undefined }; +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>args : any[] +>c : string + +o3.b?.().c; +>o3.b?.().c : string | undefined +>o3.b?.() : { c: string; } | undefined +>o3.b : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>c : string | undefined + +o3.b?.(1).c; +>o3.b?.(1).c : string | undefined +>o3.b?.(1) : { c: string; } | undefined +>o3.b : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>1 : 1 +>c : string | undefined + +o3.b?.(...[1, 2]).c; +>o3.b?.(...[1, 2]).c : string | undefined +>o3.b?.(...[1, 2]) : { c: string; } | undefined +>o3.b : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>...[1, 2] : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 +>c : string | undefined + +o3.b?.(1, ...[2, 3], 4).c; +>o3.b?.(1, ...[2, 3], 4).c : string | undefined +>o3.b?.(1, ...[2, 3], 4) : { c: string; } | undefined +>o3.b : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>1 : 1 +>...[2, 3] : number +>[2, 3] : number[] +>2 : 2 +>3 : 3 +>4 : 4 +>c : string | undefined + +o3.b?.()["c"]; +>o3.b?.()["c"] : string | undefined +>o3.b?.() : { c: string; } | undefined +>o3.b : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>"c" : "c" + +o3.b?.(1)["c"]; +>o3.b?.(1)["c"] : string | undefined +>o3.b?.(1) : { c: string; } | undefined +>o3.b : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>1 : 1 +>"c" : "c" + +o3.b?.(...[1, 2])["c"]; +>o3.b?.(...[1, 2])["c"] : string | undefined +>o3.b?.(...[1, 2]) : { c: string; } | undefined +>o3.b : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>...[1, 2] : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 +>"c" : "c" + +o3.b?.(1, ...[2, 3], 4)["c"]; +>o3.b?.(1, ...[2, 3], 4)["c"] : string | undefined +>o3.b?.(1, ...[2, 3], 4) : { c: string; } | undefined +>o3.b : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>b : ((...args: any[]) => { c: string; }) | undefined +>1 : 1 +>...[2, 3] : number +>[2, 3] : number[] +>2 : 2 +>3 : 3 +>4 : 4 +>"c" : "c" + +o3["b"]?.().c; +>o3["b"]?.().c : string | undefined +>o3["b"]?.() : { c: string; } | undefined +>o3["b"] : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>"b" : "b" +>c : string | undefined + +o3["b"]?.(1).c; +>o3["b"]?.(1).c : string | undefined +>o3["b"]?.(1) : { c: string; } | undefined +>o3["b"] : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>"b" : "b" +>1 : 1 +>c : string | undefined + +o3["b"]?.(...[1, 2]).c; +>o3["b"]?.(...[1, 2]).c : string | undefined +>o3["b"]?.(...[1, 2]) : { c: string; } | undefined +>o3["b"] : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>"b" : "b" +>...[1, 2] : number +>[1, 2] : number[] +>1 : 1 +>2 : 2 +>c : string | undefined + +o3["b"]?.(1, ...[2, 3], 4).c; +>o3["b"]?.(1, ...[2, 3], 4).c : string | undefined +>o3["b"]?.(1, ...[2, 3], 4) : { c: string; } | undefined +>o3["b"] : ((...args: any[]) => { c: string; }) | undefined +>o3 : { b: ((...args: any[]) => { c: string; }) | undefined; } +>"b" : "b" +>1 : 1 +>...[2, 3] : number +>[2, 3] : number[] +>2 : 2 +>3 : 3 +>4 : 4 +>c : string | undefined + +declare const o4: undefined | ((f: (a: T) => T) => T); +>o4 : ((f: (a: T) => T) => T) | undefined +>f : (a: T) => T +>a : T + +declare function incr(x: number): number; +>incr : (x: number) => number +>x : number + +const v: number | undefined = o4?.(incr); +>v : number | undefined +>o4?.(incr) : number | undefined +>o4 : ((f: (a: T) => T) => T) | undefined +>incr : (x: number) => number + diff --git a/tests/baselines/reference/controlFlowOptionalChain.errors.txt b/tests/baselines/reference/controlFlowOptionalChain.errors.txt new file mode 100644 index 0000000000000..ce46b232b3d81 --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain.errors.txt @@ -0,0 +1,230 @@ +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(10,1): error TS2454: Variable 'a' is used before being assigned. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(14,1): error TS2454: Variable 'b' is used before being assigned. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(18,1): error TS2454: Variable 'c' is used before being assigned. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(22,1): error TS2454: Variable 'd' is used before being assigned. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(35,5): error TS2722: Cannot invoke an object which is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(39,1): error TS2722: Cannot invoke an object which is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(52,5): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(57,1): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(62,5): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(68,5): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(72,1): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(83,5): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(87,1): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(104,5): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(105,5): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(105,5): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(111,1): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(112,1): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(112,1): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(130,5): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(134,1): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(153,9): error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. + + +==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (22 errors) ==== + // assignments in shortcutting chain + declare const o: undefined | { + [key: string]: any; + [key: number]: any; + (...args: any[]): any; + }; + + let a: number; + o?.[a = 1]; + a.toString(); + ~ +!!! error TS2454: Variable 'a' is used before being assigned. + + let b: number; + o?.x[b = 1]; + b.toString(); + ~ +!!! error TS2454: Variable 'b' is used before being assigned. + + let c: number; + o?.(c = 1) + c.toString(); + ~ +!!! error TS2454: Variable 'c' is used before being assigned. + + let d: number; + o?.x(d = 1); + d.toString(); + ~ +!!! error TS2454: Variable 'd' is used before being assigned. + + // type predicates + declare const f: undefined | ((x: any) => x is number); + declare const x: string | number; + if (f?.(x)) { + x; // number + f; // (x: any) => x is number + f(x); + } + else { + x; + f; + f(x); + ~ +!!! error TS2722: Cannot invoke an object which is possibly 'undefined'. + } + x; + f; + f(x); + ~ +!!! error TS2722: Cannot invoke an object which is possibly 'undefined'. + + declare const o2: { f(x: any): x is number; } | undefined; + if (o2?.f(x)) { + x; // number + o2.f; // (x: any) => x is number + o2?.f; + o2?.f(x); + } + else { + x; + o2; + o2?.f; + o2.f; + ~~ +!!! error TS2532: Object is possibly 'undefined'. + } + x; + o2; + o2?.f; + o2.f; + ~~ +!!! error TS2532: Object is possibly 'undefined'. + + declare const o3: { x: 1, y: string } | { x: 2, y: number } | undefined; + if (o3?.x === 1) { + o3; // TODO: should be `{ x: y, y: string }` + o3.x; // TODO: should not be an error. + ~~ +!!! error TS2532: Object is possibly 'undefined'. + o3?.x; + } + else { + o3; + o3?.x; + o3.x; + ~~ +!!! error TS2532: Object is possibly 'undefined'. + } + o3; + o3?.x; + o3.x; + ~~ +!!! error TS2532: Object is possibly 'undefined'. + + declare const o4: { x?: { y: boolean } }; + if (o4.x?.y) { + o4.x; // { y: boolean } + o4.x.y; // true + o4.x?.y; // true + } + else { + o4.x; + o4.x?.y; + o4.x.y; + ~~~~ +!!! error TS2532: Object is possibly 'undefined'. + } + o4.x; + o4.x?.y; + o4.x.y; + ~~~~ +!!! error TS2532: Object is possibly 'undefined'. + + declare const o5: { x?: { y: { z?: { w: boolean } } } }; + if (o5.x?.y.z?.w) { + o5.x; + o5.x.y; + o5.x.y.z; + o5.x.y.z.w; // true + o5.x.y.z?.w; // true + o5.x?.y.z.w; // true + o5.x?.y.z?.w; // true + } + else { + o5.x; + o5.x?.y; + o5.x?.y.z; + o5.x?.y.z?.w; + o5.x.y; + ~~~~ +!!! error TS2532: Object is possibly 'undefined'. + o5.x.y.z.w; + ~~~~ +!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~ +!!! error TS2532: Object is possibly 'undefined'. + } + o5.x; + o5.x?.y; + o5.x?.y.z; + o5.x?.y.z?.w; + o5.x.y; + ~~~~ +!!! error TS2532: Object is possibly 'undefined'. + o5.x.y.z.w; + ~~~~ +!!! error TS2532: Object is possibly 'undefined'. + ~~~~~~~~ +!!! error TS2532: Object is possibly 'undefined'. + + interface Base { + f(): this is Derived; + } + + interface Derived extends Base { + x: number; + } + + declare const o6: Base | undefined; + if (o6?.f()) { + o6; // Derived + o6.f; + } + else { + o6; + o6?.f; + o6.f; + ~~ +!!! error TS2532: Object is possibly 'undefined'. + } + o6; + o6?.f; + o6.f; + ~~ +!!! error TS2532: Object is possibly 'undefined'. + + // asserts + declare const isDefined: (value: T) => asserts value is NonNullable; + declare const isString: (value: unknown) => asserts value is string; + declare const maybeIsString: undefined | ((value: unknown) => asserts value is string); + declare const maybeNever: undefined | (() => never); + + function f01(x: unknown) { + if (!!true) { + isString?.(x); + x; + } + if (!!true) { + maybeIsString?.(x); + x; + } + if (!!true) { + isDefined(maybeIsString); + maybeIsString?.(x); + ~~~~~~~~~~~~~ +!!! error TS2775: Assertions require every name in the call target to be declared with an explicit type annotation. + x; + } + if (!!true) { + maybeNever?.(); + x; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowOptionalChain.js b/tests/baselines/reference/controlFlowOptionalChain.js new file mode 100644 index 0000000000000..58a2873f93aa1 --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain.js @@ -0,0 +1,288 @@ +//// [controlFlowOptionalChain.ts] +// assignments in shortcutting chain +declare const o: undefined | { + [key: string]: any; + [key: number]: any; + (...args: any[]): any; +}; + +let a: number; +o?.[a = 1]; +a.toString(); + +let b: number; +o?.x[b = 1]; +b.toString(); + +let c: number; +o?.(c = 1) +c.toString(); + +let d: number; +o?.x(d = 1); +d.toString(); + +// type predicates +declare const f: undefined | ((x: any) => x is number); +declare const x: string | number; +if (f?.(x)) { + x; // number + f; // (x: any) => x is number + f(x); +} +else { + x; + f; + f(x); +} +x; +f; +f(x); + +declare const o2: { f(x: any): x is number; } | undefined; +if (o2?.f(x)) { + x; // number + o2.f; // (x: any) => x is number + o2?.f; + o2?.f(x); +} +else { + x; + o2; + o2?.f; + o2.f; +} +x; +o2; +o2?.f; +o2.f; + +declare const o3: { x: 1, y: string } | { x: 2, y: number } | undefined; +if (o3?.x === 1) { + o3; // TODO: should be `{ x: y, y: string }` + o3.x; // TODO: should not be an error. + o3?.x; +} +else { + o3; + o3?.x; + o3.x; +} +o3; +o3?.x; +o3.x; + +declare const o4: { x?: { y: boolean } }; +if (o4.x?.y) { + o4.x; // { y: boolean } + o4.x.y; // true + o4.x?.y; // true +} +else { + o4.x; + o4.x?.y; + o4.x.y; +} +o4.x; +o4.x?.y; +o4.x.y; + +declare const o5: { x?: { y: { z?: { w: boolean } } } }; +if (o5.x?.y.z?.w) { + o5.x; + o5.x.y; + o5.x.y.z; + o5.x.y.z.w; // true + o5.x.y.z?.w; // true + o5.x?.y.z.w; // true + o5.x?.y.z?.w; // true +} +else { + o5.x; + o5.x?.y; + o5.x?.y.z; + o5.x?.y.z?.w; + o5.x.y; + o5.x.y.z.w; +} +o5.x; +o5.x?.y; +o5.x?.y.z; +o5.x?.y.z?.w; +o5.x.y; +o5.x.y.z.w; + +interface Base { + f(): this is Derived; +} + +interface Derived extends Base { + x: number; +} + +declare const o6: Base | undefined; +if (o6?.f()) { + o6; // Derived + o6.f; +} +else { + o6; + o6?.f; + o6.f; +} +o6; +o6?.f; +o6.f; + +// asserts +declare const isDefined: (value: T) => asserts value is NonNullable; +declare const isString: (value: unknown) => asserts value is string; +declare const maybeIsString: undefined | ((value: unknown) => asserts value is string); +declare const maybeNever: undefined | (() => never); + +function f01(x: unknown) { + if (!!true) { + isString?.(x); + x; + } + if (!!true) { + maybeIsString?.(x); + x; + } + if (!!true) { + isDefined(maybeIsString); + maybeIsString?.(x); + x; + } + if (!!true) { + maybeNever?.(); + x; + } +} + + +//// [controlFlowOptionalChain.js] +"use strict"; +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10; +var a; +(_a = o) === null || _a === void 0 ? void 0 : _a[a = 1]; +a.toString(); +var b; +(_b = o) === null || _b === void 0 ? void 0 : _b.x[b = 1]; +b.toString(); +var c; +(_c = o) === null || _c === void 0 ? void 0 : _c(c = 1); +c.toString(); +var d; +(_d = o) === null || _d === void 0 ? void 0 : _d.x(d = 1); +d.toString(); +if ((_e = f) === null || _e === void 0 ? void 0 : _e(x)) { + x; // number + f; // (x: any) => x is number + f(x); +} +else { + x; + f; + f(x); +} +x; +f; +f(x); +if ((_f = o2) === null || _f === void 0 ? void 0 : _f.f(x)) { + x; // number + o2.f; // (x: any) => x is number + (_g = o2) === null || _g === void 0 ? void 0 : _g.f; + (_h = o2) === null || _h === void 0 ? void 0 : _h.f(x); +} +else { + x; + o2; + (_j = o2) === null || _j === void 0 ? void 0 : _j.f; + o2.f; +} +x; +o2; +(_k = o2) === null || _k === void 0 ? void 0 : _k.f; +o2.f; +if (((_l = o3) === null || _l === void 0 ? void 0 : _l.x) === 1) { + o3; // TODO: should be `{ x: y, y: string }` + o3.x; // TODO: should not be an error. + (_m = o3) === null || _m === void 0 ? void 0 : _m.x; +} +else { + o3; + (_o = o3) === null || _o === void 0 ? void 0 : _o.x; + o3.x; +} +o3; +(_p = o3) === null || _p === void 0 ? void 0 : _p.x; +o3.x; +if ((_q = o4.x) === null || _q === void 0 ? void 0 : _q.y) { + o4.x; // { y: boolean } + o4.x.y; // true + (_r = o4.x) === null || _r === void 0 ? void 0 : _r.y; // true +} +else { + o4.x; + (_s = o4.x) === null || _s === void 0 ? void 0 : _s.y; + o4.x.y; +} +o4.x; +(_t = o4.x) === null || _t === void 0 ? void 0 : _t.y; +o4.x.y; +if ((_v = (_u = o5.x) === null || _u === void 0 ? void 0 : _u.y.z) === null || _v === void 0 ? void 0 : _v.w) { + o5.x; + o5.x.y; + o5.x.y.z; + o5.x.y.z.w; // true + (_w = o5.x.y.z) === null || _w === void 0 ? void 0 : _w.w; // true + (_x = o5.x) === null || _x === void 0 ? void 0 : _x.y.z.w; // true + (_z = (_y = o5.x) === null || _y === void 0 ? void 0 : _y.y.z) === null || _z === void 0 ? void 0 : _z.w; // true +} +else { + o5.x; + (_0 = o5.x) === null || _0 === void 0 ? void 0 : _0.y; + (_1 = o5.x) === null || _1 === void 0 ? void 0 : _1.y.z; + (_3 = (_2 = o5.x) === null || _2 === void 0 ? void 0 : _2.y.z) === null || _3 === void 0 ? void 0 : _3.w; + o5.x.y; + o5.x.y.z.w; +} +o5.x; +(_4 = o5.x) === null || _4 === void 0 ? void 0 : _4.y; +(_5 = o5.x) === null || _5 === void 0 ? void 0 : _5.y.z; +(_7 = (_6 = o5.x) === null || _6 === void 0 ? void 0 : _6.y.z) === null || _7 === void 0 ? void 0 : _7.w; +o5.x.y; +o5.x.y.z.w; +if ((_8 = o6) === null || _8 === void 0 ? void 0 : _8.f()) { + o6; // Derived + o6.f; +} +else { + o6; + (_9 = o6) === null || _9 === void 0 ? void 0 : _9.f; + o6.f; +} +o6; +(_10 = o6) === null || _10 === void 0 ? void 0 : _10.f; +o6.f; +function f01(x) { + var _a, _b, _c, _d; + if (!!true) { + (_a = isString) === null || _a === void 0 ? void 0 : _a(x); + x; + } + if (!!true) { + (_b = maybeIsString) === null || _b === void 0 ? void 0 : _b(x); + x; + } + if (!!true) { + isDefined(maybeIsString); + (_c = maybeIsString) === null || _c === void 0 ? void 0 : _c(x); + x; + } + if (!!true) { + (_d = maybeNever) === null || _d === void 0 ? void 0 : _d(); + x; + } +} diff --git a/tests/baselines/reference/controlFlowOptionalChain.symbols b/tests/baselines/reference/controlFlowOptionalChain.symbols new file mode 100644 index 0000000000000..21f1de2a41026 --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain.symbols @@ -0,0 +1,602 @@ +=== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts === +// assignments in shortcutting chain +declare const o: undefined | { +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 1, 13)) + + [key: string]: any; +>key : Symbol(key, Decl(controlFlowOptionalChain.ts, 2, 5)) + + [key: number]: any; +>key : Symbol(key, Decl(controlFlowOptionalChain.ts, 3, 5)) + + (...args: any[]): any; +>args : Symbol(args, Decl(controlFlowOptionalChain.ts, 4, 5)) + +}; + +let a: number; +>a : Symbol(a, Decl(controlFlowOptionalChain.ts, 7, 3)) + +o?.[a = 1]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 1, 13)) +>a : Symbol(a, Decl(controlFlowOptionalChain.ts, 7, 3)) + +a.toString(); +>a.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(controlFlowOptionalChain.ts, 7, 3)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +let b: number; +>b : Symbol(b, Decl(controlFlowOptionalChain.ts, 11, 3)) + +o?.x[b = 1]; +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 1, 13)) +>b : Symbol(b, Decl(controlFlowOptionalChain.ts, 11, 3)) + +b.toString(); +>b.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>b : Symbol(b, Decl(controlFlowOptionalChain.ts, 11, 3)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +let c: number; +>c : Symbol(c, Decl(controlFlowOptionalChain.ts, 15, 3)) + +o?.(c = 1) +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 1, 13)) +>c : Symbol(c, Decl(controlFlowOptionalChain.ts, 15, 3)) + +c.toString(); +>c.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>c : Symbol(c, Decl(controlFlowOptionalChain.ts, 15, 3)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +let d: number; +>d : Symbol(d, Decl(controlFlowOptionalChain.ts, 19, 3)) + +o?.x(d = 1); +>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 1, 13)) +>d : Symbol(d, Decl(controlFlowOptionalChain.ts, 19, 3)) + +d.toString(); +>d.toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) +>d : Symbol(d, Decl(controlFlowOptionalChain.ts, 19, 3)) +>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --)) + +// type predicates +declare const f: undefined | ((x: any) => x is number); +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 24, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 24, 31)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 24, 31)) + +declare const x: string | number; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + +if (f?.(x)) { +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 24, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + + x; // number +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + + f; // (x: any) => x is number +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 24, 13)) + + f(x); +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 24, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) +} +else { + x; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + + f; +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 24, 13)) + + f(x); +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 24, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) +} +x; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + +f; +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 24, 13)) + +f(x); +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 24, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + +declare const o2: { f(x: any): x is number; } | undefined; +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 40, 22)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 40, 22)) + +if (o2?.f(x)) { +>o2?.f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + + x; // number +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + + o2.f; // (x: any) => x is number +>o2.f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) + + o2?.f; +>o2?.f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) + + o2?.f(x); +>o2?.f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) +} +else { + x; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + + o2; +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) + + o2?.f; +>o2?.f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) + + o2.f; +>o2.f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +} +x; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 25, 13)) + +o2; +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) + +o2?.f; +>o2?.f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) + +o2.f; +>o2.f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) +>o2 : Symbol(o2, Decl(controlFlowOptionalChain.ts, 40, 13)) +>f : Symbol(f, Decl(controlFlowOptionalChain.ts, 40, 19)) + +declare const o3: { x: 1, y: string } | { x: 2, y: number } | undefined; +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 58, 25)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 41)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 58, 47)) + +if (o3?.x === 1) { +>o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) + + o3; // TODO: should be `{ x: y, y: string }` +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) + + o3.x; // TODO: should not be an error. +>o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) + + o3?.x; +>o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +} +else { + o3; +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) + + o3?.x; +>o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) + + o3.x; +>o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +} +o3; +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) + +o3?.x; +>o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) + +o3.x; +>o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) +>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41)) + +declare const o4: { x?: { y: boolean } }; +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) + +if (o4.x?.y) { +>o4.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) + + o4.x; // { y: boolean } +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) + + o4.x.y; // true +>o4.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) + + o4.x?.y; // true +>o4.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +} +else { + o4.x; +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) + + o4.x?.y; +>o4.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) + + o4.x.y; +>o4.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +} +o4.x; +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) + +o4.x?.y; +>o4.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) + +o4.x.y; +>o4.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) +>o4.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>o4 : Symbol(o4, Decl(controlFlowOptionalChain.ts, 73, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 73, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 73, 25)) + +declare const o5: { x?: { y: { z?: { w: boolean } } } }; +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) + +if (o5.x?.y.z?.w) { +>o5.x?.y.z?.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x?.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) + + o5.x; +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) + + o5.x.y; +>o5.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) + + o5.x.y.z; +>o5.x.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) + + o5.x.y.z.w; // true +>o5.x.y.z.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) + + o5.x.y.z?.w; // true +>o5.x.y.z?.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) + + o5.x?.y.z.w; // true +>o5.x?.y.z.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x?.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) + + o5.x?.y.z?.w; // true +>o5.x?.y.z?.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x?.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +} +else { + o5.x; +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) + + o5.x?.y; +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) + + o5.x?.y.z; +>o5.x?.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) + + o5.x?.y.z?.w; +>o5.x?.y.z?.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x?.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) + + o5.x.y; +>o5.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) + + o5.x.y.z.w; +>o5.x.y.z.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +} +o5.x; +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) + +o5.x?.y; +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) + +o5.x?.y.z; +>o5.x?.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) + +o5.x?.y.z?.w; +>o5.x?.y.z?.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x?.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x?.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) + +o5.x.y; +>o5.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) + +o5.x.y.z.w; +>o5.x.y.z.w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) +>o5.x.y.z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>o5.x.y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>o5.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>o5 : Symbol(o5, Decl(controlFlowOptionalChain.ts, 88, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 88, 19)) +>y : Symbol(y, Decl(controlFlowOptionalChain.ts, 88, 25)) +>z : Symbol(z, Decl(controlFlowOptionalChain.ts, 88, 30)) +>w : Symbol(w, Decl(controlFlowOptionalChain.ts, 88, 36)) + +interface Base { +>Base : Symbol(Base, Decl(controlFlowOptionalChain.ts, 111, 11)) + + f(): this is Derived; +>f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +>Derived : Symbol(Derived, Decl(controlFlowOptionalChain.ts, 115, 1)) +} + +interface Derived extends Base { +>Derived : Symbol(Derived, Decl(controlFlowOptionalChain.ts, 115, 1)) +>Base : Symbol(Base, Decl(controlFlowOptionalChain.ts, 111, 11)) + + x: number; +>x : Symbol(Derived.x, Decl(controlFlowOptionalChain.ts, 117, 32)) +} + +declare const o6: Base | undefined; +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) +>Base : Symbol(Base, Decl(controlFlowOptionalChain.ts, 111, 11)) + +if (o6?.f()) { +>o6?.f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) +>f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) + + o6; // Derived +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) + + o6.f; +>o6.f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) +>f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +} +else { + o6; +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) + + o6?.f; +>o6?.f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) +>f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) + + o6.f; +>o6.f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) +>f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +} +o6; +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) + +o6?.f; +>o6?.f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) +>f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) + +o6.f; +>o6.f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) +>o6 : Symbol(o6, Decl(controlFlowOptionalChain.ts, 121, 13)) +>f : Symbol(Base.f, Decl(controlFlowOptionalChain.ts, 113, 16)) + +// asserts +declare const isDefined: (value: T) => asserts value is NonNullable; +>isDefined : Symbol(isDefined, Decl(controlFlowOptionalChain.ts, 136, 13)) +>T : Symbol(T, Decl(controlFlowOptionalChain.ts, 136, 26)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 136, 29)) +>T : Symbol(T, Decl(controlFlowOptionalChain.ts, 136, 26)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 136, 29)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(controlFlowOptionalChain.ts, 136, 26)) + +declare const isString: (value: unknown) => asserts value is string; +>isString : Symbol(isString, Decl(controlFlowOptionalChain.ts, 137, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 137, 25)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 137, 25)) + +declare const maybeIsString: undefined | ((value: unknown) => asserts value is string); +>maybeIsString : Symbol(maybeIsString, Decl(controlFlowOptionalChain.ts, 138, 13)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 138, 43)) +>value : Symbol(value, Decl(controlFlowOptionalChain.ts, 138, 43)) + +declare const maybeNever: undefined | (() => never); +>maybeNever : Symbol(maybeNever, Decl(controlFlowOptionalChain.ts, 139, 13)) + +function f01(x: unknown) { +>f01 : Symbol(f01, Decl(controlFlowOptionalChain.ts, 139, 52)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 141, 13)) + + if (!!true) { + isString?.(x); +>isString : Symbol(isString, Decl(controlFlowOptionalChain.ts, 137, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 141, 13)) + + x; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 141, 13)) + } + if (!!true) { + maybeIsString?.(x); +>maybeIsString : Symbol(maybeIsString, Decl(controlFlowOptionalChain.ts, 138, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 141, 13)) + + x; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 141, 13)) + } + if (!!true) { + isDefined(maybeIsString); +>isDefined : Symbol(isDefined, Decl(controlFlowOptionalChain.ts, 136, 13)) +>maybeIsString : Symbol(maybeIsString, Decl(controlFlowOptionalChain.ts, 138, 13)) + + maybeIsString?.(x); +>maybeIsString : Symbol(maybeIsString, Decl(controlFlowOptionalChain.ts, 138, 13)) +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 141, 13)) + + x; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 141, 13)) + } + if (!!true) { + maybeNever?.(); +>maybeNever : Symbol(maybeNever, Decl(controlFlowOptionalChain.ts, 139, 13)) + + x; +>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 141, 13)) + } +} + diff --git a/tests/baselines/reference/controlFlowOptionalChain.types b/tests/baselines/reference/controlFlowOptionalChain.types new file mode 100644 index 0000000000000..a93c5b0b43f04 --- /dev/null +++ b/tests/baselines/reference/controlFlowOptionalChain.types @@ -0,0 +1,636 @@ +=== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts === +// assignments in shortcutting chain +declare const o: undefined | { +>o : { (...args: any[]): any; [key: string]: any; [key: number]: any; } | undefined + + [key: string]: any; +>key : string + + [key: number]: any; +>key : number + + (...args: any[]): any; +>args : any[] + +}; + +let a: number; +>a : number + +o?.[a = 1]; +>o?.[a = 1] : any +>o : { (...args: any[]): any; [key: string]: any; [key: number]: any; } | undefined +>a = 1 : 1 +>a : number +>1 : 1 + +a.toString(); +>a.toString() : string +>a.toString : (radix?: number | undefined) => string +>a : number +>toString : (radix?: number | undefined) => string + +let b: number; +>b : number + +o?.x[b = 1]; +>o?.x[b = 1] : any +>o?.x : any +>o : { (...args: any[]): any; [key: string]: any; [key: number]: any; } | undefined +>x : any +>b = 1 : 1 +>b : number +>1 : 1 + +b.toString(); +>b.toString() : string +>b.toString : (radix?: number | undefined) => string +>b : number +>toString : (radix?: number | undefined) => string + +let c: number; +>c : number + +o?.(c = 1) +>o?.(c = 1) : any +>o : { (...args: any[]): any; [key: string]: any; [key: number]: any; } | undefined +>c = 1 : 1 +>c : number +>1 : 1 + +c.toString(); +>c.toString() : string +>c.toString : (radix?: number | undefined) => string +>c : number +>toString : (radix?: number | undefined) => string + +let d: number; +>d : number + +o?.x(d = 1); +>o?.x(d = 1) : any +>o?.x : any +>o : { (...args: any[]): any; [key: string]: any; [key: number]: any; } | undefined +>x : any +>d = 1 : 1 +>d : number +>1 : 1 + +d.toString(); +>d.toString() : string +>d.toString : (radix?: number | undefined) => string +>d : number +>toString : (radix?: number | undefined) => string + +// type predicates +declare const f: undefined | ((x: any) => x is number); +>f : ((x: any) => x is number) | undefined +>x : any + +declare const x: string | number; +>x : string | number + +if (f?.(x)) { +>f?.(x) : boolean | undefined +>f : ((x: any) => x is number) | undefined +>x : string | number + + x; // number +>x : number + + f; // (x: any) => x is number +>f : (x: any) => x is number + + f(x); +>f(x) : boolean +>f : (x: any) => x is number +>x : number +} +else { + x; +>x : string | number + + f; +>f : ((x: any) => x is number) | undefined + + f(x); +>f(x) : boolean +>f : ((x: any) => x is number) | undefined +>x : string | number +} +x; +>x : string | number + +f; +>f : ((x: any) => x is number) | undefined + +f(x); +>f(x) : boolean +>f : ((x: any) => x is number) | undefined +>x : string | number + +declare const o2: { f(x: any): x is number; } | undefined; +>o2 : { f(x: any): x is number; } | undefined +>f : (x: any) => x is number +>x : any + +if (o2?.f(x)) { +>o2?.f(x) : boolean | undefined +>o2?.f : ((x: any) => x is number) | undefined +>o2 : { f(x: any): x is number; } | undefined +>f : ((x: any) => x is number) | undefined +>x : string | number + + x; // number +>x : number + + o2.f; // (x: any) => x is number +>o2.f : (x: any) => x is number +>o2 : { f(x: any): x is number; } +>f : (x: any) => x is number + + o2?.f; +>o2?.f : (x: any) => x is number +>o2 : { f(x: any): x is number; } +>f : (x: any) => x is number + + o2?.f(x); +>o2?.f(x) : boolean +>o2?.f : (x: any) => x is number +>o2 : { f(x: any): x is number; } +>f : (x: any) => x is number +>x : number +} +else { + x; +>x : string | number + + o2; +>o2 : { f(x: any): x is number; } | undefined + + o2?.f; +>o2?.f : ((x: any) => x is number) | undefined +>o2 : { f(x: any): x is number; } | undefined +>f : ((x: any) => x is number) | undefined + + o2.f; +>o2.f : (x: any) => x is number +>o2 : { f(x: any): x is number; } | undefined +>f : (x: any) => x is number +} +x; +>x : string | number + +o2; +>o2 : { f(x: any): x is number; } | undefined + +o2?.f; +>o2?.f : ((x: any) => x is number) | undefined +>o2 : { f(x: any): x is number; } | undefined +>f : ((x: any) => x is number) | undefined + +o2.f; +>o2.f : (x: any) => x is number +>o2 : { f(x: any): x is number; } | undefined +>f : (x: any) => x is number + +declare const o3: { x: 1, y: string } | { x: 2, y: number } | undefined; +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>x : 1 +>y : string +>x : 2 +>y : number + +if (o3?.x === 1) { +>o3?.x === 1 : boolean +>o3?.x : 1 | 2 | undefined +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>x : 1 | 2 | undefined +>1 : 1 + + o3; // TODO: should be `{ x: y, y: string }` +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined + + o3.x; // TODO: should not be an error. +>o3.x : 1 +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>x : 1 + + o3?.x; +>o3?.x : 1 | undefined +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>x : 1 | undefined +} +else { + o3; +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined + + o3?.x; +>o3?.x : 2 | undefined +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>x : 2 | undefined + + o3.x; +>o3.x : 2 +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>x : 2 +} +o3; +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined + +o3?.x; +>o3?.x : 1 | 2 | undefined +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>x : 1 | 2 | undefined + +o3.x; +>o3.x : 1 | 2 +>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined +>x : 1 | 2 + +declare const o4: { x?: { y: boolean } }; +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } | undefined +>y : boolean + +if (o4.x?.y) { +>o4.x?.y : boolean | undefined +>o4.x : { y: boolean; } | undefined +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } | undefined +>y : boolean | undefined + + o4.x; // { y: boolean } +>o4.x : { y: boolean; } +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } + + o4.x.y; // true +>o4.x.y : true +>o4.x : { y: boolean; } +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } +>y : true + + o4.x?.y; // true +>o4.x?.y : true +>o4.x : { y: boolean; } +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } +>y : true +} +else { + o4.x; +>o4.x : { y: boolean; } | undefined +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } | undefined + + o4.x?.y; +>o4.x?.y : boolean | undefined +>o4.x : { y: boolean; } | undefined +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } | undefined +>y : boolean | undefined + + o4.x.y; +>o4.x.y : boolean +>o4.x : { y: boolean; } | undefined +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } | undefined +>y : boolean +} +o4.x; +>o4.x : { y: boolean; } | undefined +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } | undefined + +o4.x?.y; +>o4.x?.y : boolean | undefined +>o4.x : { y: boolean; } | undefined +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } | undefined +>y : boolean | undefined + +o4.x.y; +>o4.x.y : boolean +>o4.x : { y: boolean; } | undefined +>o4 : { x?: { y: boolean; } | undefined; } +>x : { y: boolean; } | undefined +>y : boolean + +declare const o5: { x?: { y: { z?: { w: boolean } } } }; +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } +>z : { w: boolean; } | undefined +>w : boolean + +if (o5.x?.y.z?.w) { +>o5.x?.y.z?.w : boolean | undefined +>o5.x?.y.z : { w: boolean; } | undefined +>o5.x?.y : { z?: { w: boolean; } | undefined; } | undefined +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } | undefined +>z : { w: boolean; } | undefined +>w : boolean | undefined + + o5.x; +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } + + o5.x.y; +>o5.x.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } +>y : { z?: { w: boolean; } | undefined; } + + o5.x.y.z; +>o5.x.y.z : { w: boolean; } +>o5.x.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } +>y : { z?: { w: boolean; } | undefined; } +>z : { w: boolean; } + + o5.x.y.z.w; // true +>o5.x.y.z.w : true +>o5.x.y.z : { w: boolean; } +>o5.x.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } +>y : { z?: { w: boolean; } | undefined; } +>z : { w: boolean; } +>w : true + + o5.x.y.z?.w; // true +>o5.x.y.z?.w : true +>o5.x.y.z : { w: boolean; } +>o5.x.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } +>y : { z?: { w: boolean; } | undefined; } +>z : { w: boolean; } +>w : true + + o5.x?.y.z.w; // true +>o5.x?.y.z.w : true +>o5.x?.y.z : { w: boolean; } +>o5.x?.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } +>y : { z?: { w: boolean; } | undefined; } +>z : { w: boolean; } +>w : true + + o5.x?.y.z?.w; // true +>o5.x?.y.z?.w : true +>o5.x?.y.z : { w: boolean; } +>o5.x?.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } +>y : { z?: { w: boolean; } | undefined; } +>z : { w: boolean; } +>w : true +} +else { + o5.x; +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined + + o5.x?.y; +>o5.x?.y : { z?: { w: boolean; } | undefined; } | undefined +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } | undefined + + o5.x?.y.z; +>o5.x?.y.z : { w: boolean; } | undefined +>o5.x?.y : { z?: { w: boolean; } | undefined; } | undefined +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } | undefined +>z : { w: boolean; } | undefined + + o5.x?.y.z?.w; +>o5.x?.y.z?.w : boolean | undefined +>o5.x?.y.z : { w: boolean; } | undefined +>o5.x?.y : { z?: { w: boolean; } | undefined; } | undefined +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } | undefined +>z : { w: boolean; } | undefined +>w : boolean | undefined + + o5.x.y; +>o5.x.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } + + o5.x.y.z.w; +>o5.x.y.z.w : boolean +>o5.x.y.z : { w: boolean; } | undefined +>o5.x.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } +>z : { w: boolean; } | undefined +>w : boolean +} +o5.x; +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined + +o5.x?.y; +>o5.x?.y : { z?: { w: boolean; } | undefined; } | undefined +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } | undefined + +o5.x?.y.z; +>o5.x?.y.z : { w: boolean; } | undefined +>o5.x?.y : { z?: { w: boolean; } | undefined; } | undefined +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } | undefined +>z : { w: boolean; } | undefined + +o5.x?.y.z?.w; +>o5.x?.y.z?.w : boolean | undefined +>o5.x?.y.z : { w: boolean; } | undefined +>o5.x?.y : { z?: { w: boolean; } | undefined; } | undefined +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } | undefined +>z : { w: boolean; } | undefined +>w : boolean | undefined + +o5.x.y; +>o5.x.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } + +o5.x.y.z.w; +>o5.x.y.z.w : boolean +>o5.x.y.z : { w: boolean; } | undefined +>o5.x.y : { z?: { w: boolean; } | undefined; } +>o5.x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>o5 : { x?: { y: { z?: { w: boolean; } | undefined; }; } | undefined; } +>x : { y: { z?: { w: boolean; } | undefined; }; } | undefined +>y : { z?: { w: boolean; } | undefined; } +>z : { w: boolean; } | undefined +>w : boolean + +interface Base { + f(): this is Derived; +>f : () => this is Derived +} + +interface Derived extends Base { + x: number; +>x : number +} + +declare const o6: Base | undefined; +>o6 : Base | undefined + +if (o6?.f()) { +>o6?.f() : boolean | undefined +>o6?.f : (() => this is Derived) | undefined +>o6 : Base | undefined +>f : (() => this is Derived) | undefined + + o6; // Derived +>o6 : Derived + + o6.f; +>o6.f : () => this is Derived +>o6 : Derived +>f : () => this is Derived +} +else { + o6; +>o6 : Base | undefined + + o6?.f; +>o6?.f : (() => this is Derived) | undefined +>o6 : Base | undefined +>f : (() => this is Derived) | undefined + + o6.f; +>o6.f : () => this is Derived +>o6 : Base | undefined +>f : () => this is Derived +} +o6; +>o6 : Base | undefined + +o6?.f; +>o6?.f : (() => this is Derived) | undefined +>o6 : Base | undefined +>f : (() => this is Derived) | undefined + +o6.f; +>o6.f : () => this is Derived +>o6 : Base | undefined +>f : () => this is Derived + +// asserts +declare const isDefined: (value: T) => asserts value is NonNullable; +>isDefined : (value: T) => asserts value is NonNullable +>value : T + +declare const isString: (value: unknown) => asserts value is string; +>isString : (value: unknown) => asserts value is string +>value : unknown + +declare const maybeIsString: undefined | ((value: unknown) => asserts value is string); +>maybeIsString : ((value: unknown) => asserts value is string) | undefined +>value : unknown + +declare const maybeNever: undefined | (() => never); +>maybeNever : (() => never) | undefined + +function f01(x: unknown) { +>f01 : (x: unknown) => void +>x : unknown + + if (!!true) { +>!!true : true +>!true : false +>true : true + + isString?.(x); +>isString?.(x) : void +>isString : (value: unknown) => asserts value is string +>x : unknown + + x; +>x : string + } + if (!!true) { +>!!true : true +>!true : false +>true : true + + maybeIsString?.(x); +>maybeIsString?.(x) : void | undefined +>maybeIsString : ((value: unknown) => asserts value is string) | undefined +>x : unknown + + x; +>x : unknown + } + if (!!true) { +>!!true : true +>!true : false +>true : true + + isDefined(maybeIsString); +>isDefined(maybeIsString) : void +>isDefined : (value: T) => asserts value is NonNullable +>maybeIsString : ((value: unknown) => asserts value is string) | undefined + + maybeIsString?.(x); +>maybeIsString?.(x) : void +>maybeIsString : (value: unknown) => asserts value is string +>x : unknown + + x; +>x : unknown + } + if (!!true) { +>!!true : true +>!true : false +>true : true + + maybeNever?.(); +>maybeNever?.() : undefined +>maybeNever : (() => never) | undefined + + x; +>x : unknown + } +} + diff --git a/tests/baselines/reference/elementAccessChain.2.js b/tests/baselines/reference/elementAccessChain.2.js new file mode 100644 index 0000000000000..4212e5a20b110 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.2.js @@ -0,0 +1,20 @@ +//// [elementAccessChain.2.ts] +declare const o1: undefined | { b: string }; +o1?.["b"]; + +declare const o2: undefined | { b: { c: string } }; +o2?.["b"].c; +o2?.b["c"]; + +declare const o3: { b: undefined | { c: string } }; +o3["b"]?.c; +o3.b?.["c"]; + + +//// [elementAccessChain.2.js] +var _a, _b, _c, _d, _e; +(_a = o1) === null || _a === void 0 ? void 0 : _a["b"]; +(_b = o2) === null || _b === void 0 ? void 0 : _b["b"].c; +(_c = o2) === null || _c === void 0 ? void 0 : _c.b["c"]; +(_d = o3["b"]) === null || _d === void 0 ? void 0 : _d.c; +(_e = o3.b) === null || _e === void 0 ? void 0 : _e["c"]; diff --git a/tests/baselines/reference/elementAccessChain.2.symbols b/tests/baselines/reference/elementAccessChain.2.symbols new file mode 100644 index 0000000000000..689e8ec94cd87 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.2.symbols @@ -0,0 +1,43 @@ +=== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts === +declare const o1: undefined | { b: string }; +>o1 : Symbol(o1, Decl(elementAccessChain.2.ts, 0, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 0, 31)) + +o1?.["b"]; +>o1 : Symbol(o1, Decl(elementAccessChain.2.ts, 0, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.2.ts, 0, 31)) + +declare const o2: undefined | { b: { c: string } }; +>o2 : Symbol(o2, Decl(elementAccessChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 3, 31)) +>c : Symbol(c, Decl(elementAccessChain.2.ts, 3, 36)) + +o2?.["b"].c; +>o2?.["b"].c : Symbol(c, Decl(elementAccessChain.2.ts, 3, 36)) +>o2 : Symbol(o2, Decl(elementAccessChain.2.ts, 3, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.2.ts, 3, 31)) +>c : Symbol(c, Decl(elementAccessChain.2.ts, 3, 36)) + +o2?.b["c"]; +>o2?.b : Symbol(b, Decl(elementAccessChain.2.ts, 3, 31)) +>o2 : Symbol(o2, Decl(elementAccessChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 3, 31)) +>"c" : Symbol(c, Decl(elementAccessChain.2.ts, 3, 36)) + +declare const o3: { b: undefined | { c: string } }; +>o3 : Symbol(o3, Decl(elementAccessChain.2.ts, 7, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 7, 19)) +>c : Symbol(c, Decl(elementAccessChain.2.ts, 7, 36)) + +o3["b"]?.c; +>o3["b"]?.c : Symbol(c, Decl(elementAccessChain.2.ts, 7, 36)) +>o3 : Symbol(o3, Decl(elementAccessChain.2.ts, 7, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.2.ts, 7, 19)) +>c : Symbol(c, Decl(elementAccessChain.2.ts, 7, 36)) + +o3.b?.["c"]; +>o3.b : Symbol(b, Decl(elementAccessChain.2.ts, 7, 19)) +>o3 : Symbol(o3, Decl(elementAccessChain.2.ts, 7, 13)) +>b : Symbol(b, Decl(elementAccessChain.2.ts, 7, 19)) +>"c" : Symbol(c, Decl(elementAccessChain.2.ts, 7, 36)) + diff --git a/tests/baselines/reference/elementAccessChain.2.types b/tests/baselines/reference/elementAccessChain.2.types new file mode 100644 index 0000000000000..ef6b56075246c --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.2.types @@ -0,0 +1,48 @@ +=== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts === +declare const o1: undefined | { b: string }; +>o1 : { b: string; } +>b : string + +o1?.["b"]; +>o1?.["b"] : string +>o1 : { b: string; } +>"b" : "b" + +declare const o2: undefined | { b: { c: string } }; +>o2 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +o2?.["b"].c; +>o2?.["b"].c : string +>o2?.["b"] : { c: string; } +>o2 : { b: { c: string; }; } +>"b" : "b" +>c : string + +o2?.b["c"]; +>o2?.b["c"] : string +>o2?.b : { c: string; } +>o2 : { b: { c: string; }; } +>b : { c: string; } +>"c" : "c" + +declare const o3: { b: undefined | { c: string } }; +>o3 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +o3["b"]?.c; +>o3["b"]?.c : string +>o3["b"] : { c: string; } +>o3 : { b: { c: string; }; } +>"b" : "b" +>c : string + +o3.b?.["c"]; +>o3.b?.["c"] : string +>o3.b : { c: string; } +>o3 : { b: { c: string; }; } +>b : { c: string; } +>"c" : "c" + diff --git a/tests/baselines/reference/elementAccessChain.3.errors.txt b/tests/baselines/reference/elementAccessChain.3.errors.txt new file mode 100644 index 0000000000000..ab8140bc0cf6f --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.3.errors.txt @@ -0,0 +1,98 @@ +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(3,1): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(4,1): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(5,1): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(6,1): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(8,3): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(9,3): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(10,3): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(11,3): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(13,1): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(14,1): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(15,1): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(16,1): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(18,6): error TS2780: The left-hand side of a 'for...in' statement may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(19,6): error TS2780: The left-hand side of a 'for...in' statement may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(20,6): error TS2781: The left-hand side of a 'for...of' statement may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(21,6): error TS2781: The left-hand side of a 'for...of' statement may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(23,7): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(24,7): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(25,7): error TS2778: The target of an object rest assignment may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(26,7): error TS2778: The target of an object rest assignment may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(27,5): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(28,5): error TS2779: The left-hand side of an assignment expression may not be an optional property access. + + +==== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts (22 errors) ==== + declare const obj: any; + + obj?.["a"]++; + ~~~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + obj?.a["b"]++; + ~~~~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + obj?.["a"]--; + ~~~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + obj?.a["b"]--; + ~~~~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + + ++obj?.["a"]; + ~~~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + ++obj?.a["b"]; + ~~~~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + --obj?.["a"]; + ~~~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + --obj?.a["b"]; + ~~~~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + + obj?.["a"] = 1; + ~~~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + obj?.a["b"] = 1; + ~~~~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + obj?.["a"] += 1; + ~~~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + obj?.a["b"] += 1; + ~~~~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + + for (obj?.["a"] in {}); + ~~~~~~~~~~ +!!! error TS2780: The left-hand side of a 'for...in' statement may not be an optional property access. + for (obj?.a["b"] in {}); + ~~~~~~~~~~~ +!!! error TS2780: The left-hand side of a 'for...in' statement may not be an optional property access. + for (obj?.["a"] of []); + ~~~~~~~~~~ +!!! error TS2781: The left-hand side of a 'for...of' statement may not be an optional property access. + for (obj?.a["b"] of []); + ~~~~~~~~~~~ +!!! error TS2781: The left-hand side of a 'for...of' statement may not be an optional property access. + + ({ a: obj?.["a"] } = { a: 1 }); + ~~~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + ({ a: obj?.a["b"] } = { a: 1 }); + ~~~~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + ({ ...obj?.["a"] } = { a: 1 }); + ~~~~~~~~~~ +!!! error TS2778: The target of an object rest assignment may not be an optional property access. + ({ ...obj?.a["b"] } = { a: 1 }); + ~~~~~~~~~~~ +!!! error TS2778: The target of an object rest assignment may not be an optional property access. + [...obj?.["a"]] = []; + ~~~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + [...obj?.a["b"]] = []; + ~~~~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + \ No newline at end of file diff --git a/tests/baselines/reference/elementAccessChain.3.js b/tests/baselines/reference/elementAccessChain.3.js new file mode 100644 index 0000000000000..4d0dd250157fc --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.3.js @@ -0,0 +1,75 @@ +//// [elementAccessChain.3.ts] +declare const obj: any; + +obj?.["a"]++; +obj?.a["b"]++; +obj?.["a"]--; +obj?.a["b"]--; + +++obj?.["a"]; +++obj?.a["b"]; +--obj?.["a"]; +--obj?.a["b"]; + +obj?.["a"] = 1; +obj?.a["b"] = 1; +obj?.["a"] += 1; +obj?.a["b"] += 1; + +for (obj?.["a"] in {}); +for (obj?.a["b"] in {}); +for (obj?.["a"] of []); +for (obj?.a["b"] of []); + +({ a: obj?.["a"] } = { a: 1 }); +({ a: obj?.a["b"] } = { a: 1 }); +({ ...obj?.["a"] } = { a: 1 }); +({ ...obj?.a["b"] } = { a: 1 }); +[...obj?.["a"]] = []; +[...obj?.a["b"]] = []; + + +//// [elementAccessChain.3.js] +"use strict"; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x; +((_a = obj) === null || _a === void 0 ? void 0 : _a["a"])++; +((_b = obj) === null || _b === void 0 ? void 0 : _b.a["b"])++; +((_c = obj) === null || _c === void 0 ? void 0 : _c["a"])--; +((_d = obj) === null || _d === void 0 ? void 0 : _d.a["b"])--; +++((_e = obj) === null || _e === void 0 ? void 0 : _e["a"]); +++((_f = obj) === null || _f === void 0 ? void 0 : _f.a["b"]); +--((_g = obj) === null || _g === void 0 ? void 0 : _g["a"]); +--((_h = obj) === null || _h === void 0 ? void 0 : _h.a["b"]); +(_j = obj) === null || _j === void 0 ? void 0 : _j["a"] = 1; +(_k = obj) === null || _k === void 0 ? void 0 : _k.a["b"] = 1; +(_l = obj) === null || _l === void 0 ? void 0 : _l["a"] += 1; +(_m = obj) === null || _m === void 0 ? void 0 : _m.a["b"] += 1; +for ((_o = obj) === null || _o === void 0 ? void 0 : _o["a"] in {}) + ; +for ((_p = obj) === null || _p === void 0 ? void 0 : _p.a["b"] in {}) + ; +for (var _i = 0, _y = []; _i < _y.length; _i++) { + (_q = obj) === null || _q === void 0 ? void 0 : _q["a"] = _y[_i]; + ; +} +for (var _z = 0, _0 = []; _z < _0.length; _z++) { + (_r = obj) === null || _r === void 0 ? void 0 : _r.a["b"] = _0[_z]; + ; +} +((_s = obj) === null || _s === void 0 ? void 0 : _s["a"] = { a: 1 }.a); +((_t = obj) === null || _t === void 0 ? void 0 : _t.a["b"] = { a: 1 }.a); +((_u = obj) === null || _u === void 0 ? void 0 : _u["a"] = __rest({ a: 1 }, [])); +((_v = obj) === null || _v === void 0 ? void 0 : _v.a["b"] = __rest({ a: 1 }, [])); +(_w = obj) === null || _w === void 0 ? void 0 : _w["a"] = [].slice(0); +(_x = obj) === null || _x === void 0 ? void 0 : _x.a["b"] = [].slice(0); diff --git a/tests/baselines/reference/elementAccessChain.3.symbols b/tests/baselines/reference/elementAccessChain.3.symbols new file mode 100644 index 0000000000000..2b63cd9731e59 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.3.symbols @@ -0,0 +1,76 @@ +=== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts === +declare const obj: any; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +obj?.["a"]++; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +obj?.a["b"]++; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +obj?.["a"]--; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +obj?.a["b"]--; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +++obj?.["a"]; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +++obj?.a["b"]; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +--obj?.["a"]; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +--obj?.a["b"]; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +obj?.["a"] = 1; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +obj?.a["b"] = 1; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +obj?.["a"] += 1; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +obj?.a["b"] += 1; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +for (obj?.["a"] in {}); +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +for (obj?.a["b"] in {}); +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +for (obj?.["a"] of []); +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +for (obj?.a["b"] of []); +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +({ a: obj?.["a"] } = { a: 1 }); +>a : Symbol(a, Decl(elementAccessChain.3.ts, 22, 2)) +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) +>a : Symbol(a, Decl(elementAccessChain.3.ts, 22, 22)) + +({ a: obj?.a["b"] } = { a: 1 }); +>a : Symbol(a, Decl(elementAccessChain.3.ts, 23, 2)) +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) +>a : Symbol(a, Decl(elementAccessChain.3.ts, 23, 23)) + +({ ...obj?.["a"] } = { a: 1 }); +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) +>a : Symbol(a, Decl(elementAccessChain.3.ts, 24, 22)) + +({ ...obj?.a["b"] } = { a: 1 }); +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) +>a : Symbol(a, Decl(elementAccessChain.3.ts, 25, 23)) + +[...obj?.["a"]] = []; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + +[...obj?.a["b"]] = []; +>obj : Symbol(obj, Decl(elementAccessChain.3.ts, 0, 13)) + diff --git a/tests/baselines/reference/elementAccessChain.3.types b/tests/baselines/reference/elementAccessChain.3.types new file mode 100644 index 0000000000000..d0d00ffea7193 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.3.types @@ -0,0 +1,190 @@ +=== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts === +declare const obj: any; +>obj : any + +obj?.["a"]++; +>obj?.["a"]++ : number +>obj?.["a"] : any +>obj : any +>"a" : "a" + +obj?.a["b"]++; +>obj?.a["b"]++ : number +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" + +obj?.["a"]--; +>obj?.["a"]-- : number +>obj?.["a"] : any +>obj : any +>"a" : "a" + +obj?.a["b"]--; +>obj?.a["b"]-- : number +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" + +++obj?.["a"]; +>++obj?.["a"] : number +>obj?.["a"] : any +>obj : any +>"a" : "a" + +++obj?.a["b"]; +>++obj?.a["b"] : number +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" + +--obj?.["a"]; +>--obj?.["a"] : number +>obj?.["a"] : any +>obj : any +>"a" : "a" + +--obj?.a["b"]; +>--obj?.a["b"] : number +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" + +obj?.["a"] = 1; +>obj?.["a"] = 1 : 1 +>obj?.["a"] : any +>obj : any +>"a" : "a" +>1 : 1 + +obj?.a["b"] = 1; +>obj?.a["b"] = 1 : 1 +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" +>1 : 1 + +obj?.["a"] += 1; +>obj?.["a"] += 1 : any +>obj?.["a"] : any +>obj : any +>"a" : "a" +>1 : 1 + +obj?.a["b"] += 1; +>obj?.a["b"] += 1 : any +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" +>1 : 1 + +for (obj?.["a"] in {}); +>obj?.["a"] : any +>obj : any +>"a" : "a" +>{} : {} + +for (obj?.a["b"] in {}); +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" +>{} : {} + +for (obj?.["a"] of []); +>obj?.["a"] : any +>obj : any +>"a" : "a" +>[] : never[] + +for (obj?.a["b"] of []); +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" +>[] : never[] + +({ a: obj?.["a"] } = { a: 1 }); +>({ a: obj?.["a"] } = { a: 1 }) : { a: number; } +>{ a: obj?.["a"] } = { a: 1 } : { a: number; } +>{ a: obj?.["a"] } : { a: any; } +>a : any +>obj?.["a"] : any +>obj : any +>"a" : "a" +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +({ a: obj?.a["b"] } = { a: 1 }); +>({ a: obj?.a["b"] } = { a: 1 }) : { a: number; } +>{ a: obj?.a["b"] } = { a: 1 } : { a: number; } +>{ a: obj?.a["b"] } : { a: any; } +>a : any +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +({ ...obj?.["a"] } = { a: 1 }); +>({ ...obj?.["a"] } = { a: 1 }) : { a: number; } +>{ ...obj?.["a"] } = { a: 1 } : { a: number; } +>{ ...obj?.["a"] } : any +>obj?.["a"] : any +>obj : any +>"a" : "a" +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +({ ...obj?.a["b"] } = { a: 1 }); +>({ ...obj?.a["b"] } = { a: 1 }) : { a: number; } +>{ ...obj?.a["b"] } = { a: 1 } : { a: number; } +>{ ...obj?.a["b"] } : any +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +[...obj?.["a"]] = []; +>[...obj?.["a"]] = [] : never[] +>[...obj?.["a"]] : never[] +>...obj?.["a"] : any +>obj?.["a"] : any +>obj : any +>"a" : "a" +>[] : never[] + +[...obj?.a["b"]] = []; +>[...obj?.a["b"]] = [] : never[] +>[...obj?.a["b"]] : never[] +>...obj?.a["b"] : any +>obj?.a["b"] : any +>obj?.a : any +>obj : any +>a : any +>"b" : "b" +>[] : never[] + diff --git a/tests/baselines/reference/elementAccessChain.js b/tests/baselines/reference/elementAccessChain.js new file mode 100644 index 0000000000000..bf7804ff2cf70 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.js @@ -0,0 +1,37 @@ +//// [elementAccessChain.ts] +declare const o1: undefined | { b: string }; +o1?.["b"]; + +declare const o2: undefined | { b: { c: string } }; +o2?.["b"].c; +o2?.b["c"]; + +declare const o3: { b: undefined | { c: string } }; +o3["b"]?.c; +o3.b?.["c"]; + +declare const o4: { b?: { c: { d?: { e: string } } } }; +o4.b?.["c"].d?.e; +o4.b?.["c"].d?.["e"]; + +declare const o5: { b?(): { c: { d?: { e: string } } } }; +o5.b?.()["c"].d?.e; +o5.b?.()["c"].d?.["e"]; +o5["b"]?.()["c"].d?.e; +o5["b"]?.()["c"].d?.["e"]; + + +//// [elementAccessChain.js] +"use strict"; +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w; +(_a = o1) === null || _a === void 0 ? void 0 : _a["b"]; +(_b = o2) === null || _b === void 0 ? void 0 : _b["b"].c; +(_c = o2) === null || _c === void 0 ? void 0 : _c.b["c"]; +(_d = o3["b"]) === null || _d === void 0 ? void 0 : _d.c; +(_e = o3.b) === null || _e === void 0 ? void 0 : _e["c"]; +(_g = (_f = o4.b) === null || _f === void 0 ? void 0 : _f["c"].d) === null || _g === void 0 ? void 0 : _g.e; +(_j = (_h = o4.b) === null || _h === void 0 ? void 0 : _h["c"].d) === null || _j === void 0 ? void 0 : _j["e"]; +(_m = (_l = (_k = o5).b) === null || _l === void 0 ? void 0 : _l.call(_k)["c"].d) === null || _m === void 0 ? void 0 : _m.e; +(_q = (_p = (_o = o5).b) === null || _p === void 0 ? void 0 : _p.call(_o)["c"].d) === null || _q === void 0 ? void 0 : _q["e"]; +(_t = (_s = (_r = o5)["b"]) === null || _s === void 0 ? void 0 : _s.call(_r)["c"].d) === null || _t === void 0 ? void 0 : _t.e; +(_w = (_v = (_u = o5)["b"]) === null || _v === void 0 ? void 0 : _v.call(_u)["c"].d) === null || _w === void 0 ? void 0 : _w["e"]; diff --git a/tests/baselines/reference/elementAccessChain.symbols b/tests/baselines/reference/elementAccessChain.symbols new file mode 100644 index 0000000000000..af253ccd4bf39 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.symbols @@ -0,0 +1,99 @@ +=== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.ts === +declare const o1: undefined | { b: string }; +>o1 : Symbol(o1, Decl(elementAccessChain.ts, 0, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 0, 31)) + +o1?.["b"]; +>o1 : Symbol(o1, Decl(elementAccessChain.ts, 0, 13)) + +declare const o2: undefined | { b: { c: string } }; +>o2 : Symbol(o2, Decl(elementAccessChain.ts, 3, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 3, 31)) +>c : Symbol(c, Decl(elementAccessChain.ts, 3, 36)) + +o2?.["b"].c; +>o2?.["b"].c : Symbol(c, Decl(elementAccessChain.ts, 3, 36)) +>o2 : Symbol(o2, Decl(elementAccessChain.ts, 3, 13)) +>c : Symbol(c, Decl(elementAccessChain.ts, 3, 36)) + +o2?.b["c"]; +>o2?.b : Symbol(b, Decl(elementAccessChain.ts, 3, 31)) +>o2 : Symbol(o2, Decl(elementAccessChain.ts, 3, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 3, 31)) + +declare const o3: { b: undefined | { c: string } }; +>o3 : Symbol(o3, Decl(elementAccessChain.ts, 7, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 7, 19)) +>c : Symbol(c, Decl(elementAccessChain.ts, 7, 36)) + +o3["b"]?.c; +>o3["b"]?.c : Symbol(c, Decl(elementAccessChain.ts, 7, 36)) +>o3 : Symbol(o3, Decl(elementAccessChain.ts, 7, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.ts, 7, 19)) +>c : Symbol(c, Decl(elementAccessChain.ts, 7, 36)) + +o3.b?.["c"]; +>o3.b : Symbol(b, Decl(elementAccessChain.ts, 7, 19)) +>o3 : Symbol(o3, Decl(elementAccessChain.ts, 7, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 7, 19)) + +declare const o4: { b?: { c: { d?: { e: string } } } }; +>o4 : Symbol(o4, Decl(elementAccessChain.ts, 11, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 11, 19)) +>c : Symbol(c, Decl(elementAccessChain.ts, 11, 25)) +>d : Symbol(d, Decl(elementAccessChain.ts, 11, 30)) +>e : Symbol(e, Decl(elementAccessChain.ts, 11, 36)) + +o4.b?.["c"].d?.e; +>o4.b?.["c"].d?.e : Symbol(e, Decl(elementAccessChain.ts, 11, 36)) +>o4.b?.["c"].d : Symbol(d, Decl(elementAccessChain.ts, 11, 30)) +>o4.b : Symbol(b, Decl(elementAccessChain.ts, 11, 19)) +>o4 : Symbol(o4, Decl(elementAccessChain.ts, 11, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 11, 19)) +>d : Symbol(d, Decl(elementAccessChain.ts, 11, 30)) +>e : Symbol(e, Decl(elementAccessChain.ts, 11, 36)) + +o4.b?.["c"].d?.["e"]; +>o4.b?.["c"].d : Symbol(d, Decl(elementAccessChain.ts, 11, 30)) +>o4.b : Symbol(b, Decl(elementAccessChain.ts, 11, 19)) +>o4 : Symbol(o4, Decl(elementAccessChain.ts, 11, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 11, 19)) +>d : Symbol(d, Decl(elementAccessChain.ts, 11, 30)) + +declare const o5: { b?(): { c: { d?: { e: string } } } }; +>o5 : Symbol(o5, Decl(elementAccessChain.ts, 15, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 15, 19)) +>c : Symbol(c, Decl(elementAccessChain.ts, 15, 27)) +>d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) +>e : Symbol(e, Decl(elementAccessChain.ts, 15, 38)) + +o5.b?.()["c"].d?.e; +>o5.b?.()["c"].d?.e : Symbol(e, Decl(elementAccessChain.ts, 15, 38)) +>o5.b?.()["c"].d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) +>o5.b : Symbol(b, Decl(elementAccessChain.ts, 15, 19)) +>o5 : Symbol(o5, Decl(elementAccessChain.ts, 15, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 15, 19)) +>d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) +>e : Symbol(e, Decl(elementAccessChain.ts, 15, 38)) + +o5.b?.()["c"].d?.["e"]; +>o5.b?.()["c"].d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) +>o5.b : Symbol(b, Decl(elementAccessChain.ts, 15, 19)) +>o5 : Symbol(o5, Decl(elementAccessChain.ts, 15, 13)) +>b : Symbol(b, Decl(elementAccessChain.ts, 15, 19)) +>d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) + +o5["b"]?.()["c"].d?.e; +>o5["b"]?.()["c"].d?.e : Symbol(e, Decl(elementAccessChain.ts, 15, 38)) +>o5["b"]?.()["c"].d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) +>o5 : Symbol(o5, Decl(elementAccessChain.ts, 15, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.ts, 15, 19)) +>d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) +>e : Symbol(e, Decl(elementAccessChain.ts, 15, 38)) + +o5["b"]?.()["c"].d?.["e"]; +>o5["b"]?.()["c"].d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) +>o5 : Symbol(o5, Decl(elementAccessChain.ts, 15, 13)) +>"b" : Symbol(b, Decl(elementAccessChain.ts, 15, 19)) +>d : Symbol(d, Decl(elementAccessChain.ts, 15, 32)) + diff --git a/tests/baselines/reference/elementAccessChain.types b/tests/baselines/reference/elementAccessChain.types new file mode 100644 index 0000000000000..0d99166ba0f08 --- /dev/null +++ b/tests/baselines/reference/elementAccessChain.types @@ -0,0 +1,132 @@ +=== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.ts === +declare const o1: undefined | { b: string }; +>o1 : { b: string; } | undefined +>b : string + +o1?.["b"]; +>o1?.["b"] : string | undefined +>o1 : { b: string; } | undefined +>"b" : "b" + +declare const o2: undefined | { b: { c: string } }; +>o2 : { b: { c: string; }; } | undefined +>b : { c: string; } +>c : string + +o2?.["b"].c; +>o2?.["b"].c : string | undefined +>o2?.["b"] : { c: string; } | undefined +>o2 : { b: { c: string; }; } | undefined +>"b" : "b" +>c : string | undefined + +o2?.b["c"]; +>o2?.b["c"] : string | undefined +>o2?.b : { c: string; } | undefined +>o2 : { b: { c: string; }; } | undefined +>b : { c: string; } | undefined +>"c" : "c" + +declare const o3: { b: undefined | { c: string } }; +>o3 : { b: { c: string; } | undefined; } +>b : { c: string; } | undefined +>c : string + +o3["b"]?.c; +>o3["b"]?.c : string | undefined +>o3["b"] : { c: string; } | undefined +>o3 : { b: { c: string; } | undefined; } +>"b" : "b" +>c : string | undefined + +o3.b?.["c"]; +>o3.b?.["c"] : string | undefined +>o3.b : { c: string; } | undefined +>o3 : { b: { c: string; } | undefined; } +>b : { c: string; } | undefined +>"c" : "c" + +declare const o4: { b?: { c: { d?: { e: string } } } }; +>o4 : { b?: { c: { d?: { e: string; } | undefined; }; } | undefined; } +>b : { c: { d?: { e: string; } | undefined; }; } | undefined +>c : { d?: { e: string; } | undefined; } +>d : { e: string; } | undefined +>e : string + +o4.b?.["c"].d?.e; +>o4.b?.["c"].d?.e : string | undefined +>o4.b?.["c"].d : { e: string; } | undefined +>o4.b?.["c"] : { d?: { e: string; } | undefined; } | undefined +>o4.b : { c: { d?: { e: string; } | undefined; }; } | undefined +>o4 : { b?: { c: { d?: { e: string; } | undefined; }; } | undefined; } +>b : { c: { d?: { e: string; } | undefined; }; } | undefined +>"c" : "c" +>d : { e: string; } | undefined +>e : string | undefined + +o4.b?.["c"].d?.["e"]; +>o4.b?.["c"].d?.["e"] : string | undefined +>o4.b?.["c"].d : { e: string; } | undefined +>o4.b?.["c"] : { d?: { e: string; } | undefined; } | undefined +>o4.b : { c: { d?: { e: string; } | undefined; }; } | undefined +>o4 : { b?: { c: { d?: { e: string; } | undefined; }; } | undefined; } +>b : { c: { d?: { e: string; } | undefined; }; } | undefined +>"c" : "c" +>d : { e: string; } | undefined +>"e" : "e" + +declare const o5: { b?(): { c: { d?: { e: string } } } }; +>o5 : { b?(): { c: { d?: { e: string; } | undefined; }; }; } +>b : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>c : { d?: { e: string; } | undefined; } +>d : { e: string; } | undefined +>e : string + +o5.b?.()["c"].d?.e; +>o5.b?.()["c"].d?.e : string | undefined +>o5.b?.()["c"].d : { e: string; } | undefined +>o5.b?.()["c"] : { d?: { e: string; } | undefined; } | undefined +>o5.b?.() : { c: { d?: { e: string; } | undefined; }; } | undefined +>o5.b : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>o5 : { b?(): { c: { d?: { e: string; } | undefined; }; }; } +>b : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>"c" : "c" +>d : { e: string; } | undefined +>e : string | undefined + +o5.b?.()["c"].d?.["e"]; +>o5.b?.()["c"].d?.["e"] : string | undefined +>o5.b?.()["c"].d : { e: string; } | undefined +>o5.b?.()["c"] : { d?: { e: string; } | undefined; } | undefined +>o5.b?.() : { c: { d?: { e: string; } | undefined; }; } | undefined +>o5.b : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>o5 : { b?(): { c: { d?: { e: string; } | undefined; }; }; } +>b : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>"c" : "c" +>d : { e: string; } | undefined +>"e" : "e" + +o5["b"]?.()["c"].d?.e; +>o5["b"]?.()["c"].d?.e : string | undefined +>o5["b"]?.()["c"].d : { e: string; } | undefined +>o5["b"]?.()["c"] : { d?: { e: string; } | undefined; } | undefined +>o5["b"]?.() : { c: { d?: { e: string; } | undefined; }; } | undefined +>o5["b"] : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>o5 : { b?(): { c: { d?: { e: string; } | undefined; }; }; } +>"b" : "b" +>"c" : "c" +>d : { e: string; } | undefined +>e : string | undefined + +o5["b"]?.()["c"].d?.["e"]; +>o5["b"]?.()["c"].d?.["e"] : string | undefined +>o5["b"]?.()["c"].d : { e: string; } | undefined +>o5["b"]?.()["c"] : { d?: { e: string; } | undefined; } | undefined +>o5["b"]?.() : { c: { d?: { e: string; } | undefined; }; } | undefined +>o5["b"] : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>o5 : { b?(): { c: { d?: { e: string; } | undefined; }; }; } +>"b" : "b" +>"c" : "c" +>d : { e: string; } | undefined +>"e" : "e" + diff --git a/tests/baselines/reference/propertyAccessChain.2.js b/tests/baselines/reference/propertyAccessChain.2.js new file mode 100644 index 0000000000000..939e9830b549f --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.2.js @@ -0,0 +1,16 @@ +//// [propertyAccessChain.2.ts] +declare const o1: undefined | { b: string }; +o1?.b; + +declare const o2: undefined | { b: { c: string } }; +o2?.b.c; + +declare const o3: { b: undefined | { c: string } }; +o3.b?.c; + + +//// [propertyAccessChain.2.js] +var _a, _b, _c; +(_a = o1) === null || _a === void 0 ? void 0 : _a.b; +(_b = o2) === null || _b === void 0 ? void 0 : _b.b.c; +(_c = o3.b) === null || _c === void 0 ? void 0 : _c.c; diff --git a/tests/baselines/reference/propertyAccessChain.2.symbols b/tests/baselines/reference/propertyAccessChain.2.symbols new file mode 100644 index 0000000000000..2d4fd96896696 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.2.symbols @@ -0,0 +1,34 @@ +=== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts === +declare const o1: undefined | { b: string }; +>o1 : Symbol(o1, Decl(propertyAccessChain.2.ts, 0, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 0, 31)) + +o1?.b; +>o1?.b : Symbol(b, Decl(propertyAccessChain.2.ts, 0, 31)) +>o1 : Symbol(o1, Decl(propertyAccessChain.2.ts, 0, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 0, 31)) + +declare const o2: undefined | { b: { c: string } }; +>o2 : Symbol(o2, Decl(propertyAccessChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 3, 31)) +>c : Symbol(c, Decl(propertyAccessChain.2.ts, 3, 36)) + +o2?.b.c; +>o2?.b.c : Symbol(c, Decl(propertyAccessChain.2.ts, 3, 36)) +>o2?.b : Symbol(b, Decl(propertyAccessChain.2.ts, 3, 31)) +>o2 : Symbol(o2, Decl(propertyAccessChain.2.ts, 3, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 3, 31)) +>c : Symbol(c, Decl(propertyAccessChain.2.ts, 3, 36)) + +declare const o3: { b: undefined | { c: string } }; +>o3 : Symbol(o3, Decl(propertyAccessChain.2.ts, 6, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 6, 19)) +>c : Symbol(c, Decl(propertyAccessChain.2.ts, 6, 36)) + +o3.b?.c; +>o3.b?.c : Symbol(c, Decl(propertyAccessChain.2.ts, 6, 36)) +>o3.b : Symbol(b, Decl(propertyAccessChain.2.ts, 6, 19)) +>o3 : Symbol(o3, Decl(propertyAccessChain.2.ts, 6, 13)) +>b : Symbol(b, Decl(propertyAccessChain.2.ts, 6, 19)) +>c : Symbol(c, Decl(propertyAccessChain.2.ts, 6, 36)) + diff --git a/tests/baselines/reference/propertyAccessChain.2.types b/tests/baselines/reference/propertyAccessChain.2.types new file mode 100644 index 0000000000000..6a046048f15a9 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.2.types @@ -0,0 +1,34 @@ +=== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts === +declare const o1: undefined | { b: string }; +>o1 : { b: string; } +>b : string + +o1?.b; +>o1?.b : string +>o1 : { b: string; } +>b : string + +declare const o2: undefined | { b: { c: string } }; +>o2 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +o2?.b.c; +>o2?.b.c : string +>o2?.b : { c: string; } +>o2 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +declare const o3: { b: undefined | { c: string } }; +>o3 : { b: { c: string; }; } +>b : { c: string; } +>c : string + +o3.b?.c; +>o3.b?.c : string +>o3.b : { c: string; } +>o3 : { b: { c: string; }; } +>b : { c: string; } +>c : string + diff --git a/tests/baselines/reference/propertyAccessChain.3.errors.txt b/tests/baselines/reference/propertyAccessChain.3.errors.txt new file mode 100644 index 0000000000000..1703ebf0e1e27 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.3.errors.txt @@ -0,0 +1,98 @@ +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(3,1): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(4,1): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(5,1): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(6,1): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(8,3): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(9,3): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(10,3): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(11,3): error TS2777: The operand of an increment or decrement operator may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(13,1): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(14,1): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(15,1): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(16,1): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(18,6): error TS2780: The left-hand side of a 'for...in' statement may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(19,6): error TS2780: The left-hand side of a 'for...in' statement may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(20,6): error TS2781: The left-hand side of a 'for...of' statement may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(21,6): error TS2781: The left-hand side of a 'for...of' statement may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(23,7): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(24,7): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(25,7): error TS2778: The target of an object rest assignment may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(26,7): error TS2778: The target of an object rest assignment may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(27,5): error TS2779: The left-hand side of an assignment expression may not be an optional property access. +tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts(28,5): error TS2779: The left-hand side of an assignment expression may not be an optional property access. + + +==== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts (22 errors) ==== + declare const obj: any; + + obj?.a++; + ~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + obj?.a.b++; + ~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + obj?.a--; + ~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + obj?.a.b--; + ~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + + ++obj?.a; + ~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + ++obj?.a.b; + ~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + --obj?.a; + ~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + --obj?.a.b; + ~~~~~~~~ +!!! error TS2777: The operand of an increment or decrement operator may not be an optional property access. + + obj?.a = 1; + ~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + obj?.a.b = 1; + ~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + obj?.a += 1; + ~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + obj?.a.b += 1; + ~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + + for (obj?.a in {}); + ~~~~~~ +!!! error TS2780: The left-hand side of a 'for...in' statement may not be an optional property access. + for (obj?.a.b in {}); + ~~~~~~~~ +!!! error TS2780: The left-hand side of a 'for...in' statement may not be an optional property access. + for (obj?.a of []); + ~~~~~~ +!!! error TS2781: The left-hand side of a 'for...of' statement may not be an optional property access. + for (obj?.a.b of []); + ~~~~~~~~ +!!! error TS2781: The left-hand side of a 'for...of' statement may not be an optional property access. + + ({ a: obj?.a } = { a: 1 }); + ~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + ({ a: obj?.a.b } = { a: 1 }); + ~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + ({ ...obj?.a } = { a: 1 }); + ~~~~~~ +!!! error TS2778: The target of an object rest assignment may not be an optional property access. + ({ ...obj?.a.b } = { a: 1 }); + ~~~~~~~~ +!!! error TS2778: The target of an object rest assignment may not be an optional property access. + [...obj?.a] = []; + ~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + [...obj?.a.b] = []; + ~~~~~~~~ +!!! error TS2779: The left-hand side of an assignment expression may not be an optional property access. + \ No newline at end of file diff --git a/tests/baselines/reference/propertyAccessChain.3.js b/tests/baselines/reference/propertyAccessChain.3.js new file mode 100644 index 0000000000000..3eedf7c2b222f --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.3.js @@ -0,0 +1,75 @@ +//// [propertyAccessChain.3.ts] +declare const obj: any; + +obj?.a++; +obj?.a.b++; +obj?.a--; +obj?.a.b--; + +++obj?.a; +++obj?.a.b; +--obj?.a; +--obj?.a.b; + +obj?.a = 1; +obj?.a.b = 1; +obj?.a += 1; +obj?.a.b += 1; + +for (obj?.a in {}); +for (obj?.a.b in {}); +for (obj?.a of []); +for (obj?.a.b of []); + +({ a: obj?.a } = { a: 1 }); +({ a: obj?.a.b } = { a: 1 }); +({ ...obj?.a } = { a: 1 }); +({ ...obj?.a.b } = { a: 1 }); +[...obj?.a] = []; +[...obj?.a.b] = []; + + +//// [propertyAccessChain.3.js] +"use strict"; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x; +((_a = obj) === null || _a === void 0 ? void 0 : _a.a)++; +((_b = obj) === null || _b === void 0 ? void 0 : _b.a.b)++; +((_c = obj) === null || _c === void 0 ? void 0 : _c.a)--; +((_d = obj) === null || _d === void 0 ? void 0 : _d.a.b)--; +++((_e = obj) === null || _e === void 0 ? void 0 : _e.a); +++((_f = obj) === null || _f === void 0 ? void 0 : _f.a.b); +--((_g = obj) === null || _g === void 0 ? void 0 : _g.a); +--((_h = obj) === null || _h === void 0 ? void 0 : _h.a.b); +(_j = obj) === null || _j === void 0 ? void 0 : _j.a = 1; +(_k = obj) === null || _k === void 0 ? void 0 : _k.a.b = 1; +(_l = obj) === null || _l === void 0 ? void 0 : _l.a += 1; +(_m = obj) === null || _m === void 0 ? void 0 : _m.a.b += 1; +for ((_o = obj) === null || _o === void 0 ? void 0 : _o.a in {}) + ; +for ((_p = obj) === null || _p === void 0 ? void 0 : _p.a.b in {}) + ; +for (var _i = 0, _y = []; _i < _y.length; _i++) { + (_q = obj) === null || _q === void 0 ? void 0 : _q.a = _y[_i]; + ; +} +for (var _z = 0, _0 = []; _z < _0.length; _z++) { + (_r = obj) === null || _r === void 0 ? void 0 : _r.a.b = _0[_z]; + ; +} +((_s = obj) === null || _s === void 0 ? void 0 : _s.a = { a: 1 }.a); +((_t = obj) === null || _t === void 0 ? void 0 : _t.a.b = { a: 1 }.a); +((_u = obj) === null || _u === void 0 ? void 0 : _u.a = __rest({ a: 1 }, [])); +((_v = obj) === null || _v === void 0 ? void 0 : _v.a.b = __rest({ a: 1 }, [])); +(_w = obj) === null || _w === void 0 ? void 0 : _w.a = [].slice(0); +(_x = obj) === null || _x === void 0 ? void 0 : _x.a.b = [].slice(0); diff --git a/tests/baselines/reference/propertyAccessChain.3.symbols b/tests/baselines/reference/propertyAccessChain.3.symbols new file mode 100644 index 0000000000000..348a436e00781 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.3.symbols @@ -0,0 +1,76 @@ +=== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts === +declare const obj: any; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +obj?.a++; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +obj?.a.b++; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +obj?.a--; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +obj?.a.b--; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +++obj?.a; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +++obj?.a.b; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +--obj?.a; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +--obj?.a.b; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +obj?.a = 1; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +obj?.a.b = 1; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +obj?.a += 1; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +obj?.a.b += 1; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +for (obj?.a in {}); +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +for (obj?.a.b in {}); +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +for (obj?.a of []); +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +for (obj?.a.b of []); +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +({ a: obj?.a } = { a: 1 }); +>a : Symbol(a, Decl(propertyAccessChain.3.ts, 22, 2)) +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) +>a : Symbol(a, Decl(propertyAccessChain.3.ts, 22, 18)) + +({ a: obj?.a.b } = { a: 1 }); +>a : Symbol(a, Decl(propertyAccessChain.3.ts, 23, 2)) +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) +>a : Symbol(a, Decl(propertyAccessChain.3.ts, 23, 20)) + +({ ...obj?.a } = { a: 1 }); +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) +>a : Symbol(a, Decl(propertyAccessChain.3.ts, 24, 18)) + +({ ...obj?.a.b } = { a: 1 }); +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) +>a : Symbol(a, Decl(propertyAccessChain.3.ts, 25, 20)) + +[...obj?.a] = []; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + +[...obj?.a.b] = []; +>obj : Symbol(obj, Decl(propertyAccessChain.3.ts, 0, 13)) + diff --git a/tests/baselines/reference/propertyAccessChain.3.types b/tests/baselines/reference/propertyAccessChain.3.types new file mode 100644 index 0000000000000..a5b97624e2475 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.3.types @@ -0,0 +1,190 @@ +=== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts === +declare const obj: any; +>obj : any + +obj?.a++; +>obj?.a++ : number +>obj?.a : any +>obj : any +>a : any + +obj?.a.b++; +>obj?.a.b++ : number +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any + +obj?.a--; +>obj?.a-- : number +>obj?.a : any +>obj : any +>a : any + +obj?.a.b--; +>obj?.a.b-- : number +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any + +++obj?.a; +>++obj?.a : number +>obj?.a : any +>obj : any +>a : any + +++obj?.a.b; +>++obj?.a.b : number +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any + +--obj?.a; +>--obj?.a : number +>obj?.a : any +>obj : any +>a : any + +--obj?.a.b; +>--obj?.a.b : number +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any + +obj?.a = 1; +>obj?.a = 1 : 1 +>obj?.a : any +>obj : any +>a : any +>1 : 1 + +obj?.a.b = 1; +>obj?.a.b = 1 : 1 +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any +>1 : 1 + +obj?.a += 1; +>obj?.a += 1 : any +>obj?.a : any +>obj : any +>a : any +>1 : 1 + +obj?.a.b += 1; +>obj?.a.b += 1 : any +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any +>1 : 1 + +for (obj?.a in {}); +>obj?.a : any +>obj : any +>a : any +>{} : {} + +for (obj?.a.b in {}); +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any +>{} : {} + +for (obj?.a of []); +>obj?.a : any +>obj : any +>a : any +>[] : never[] + +for (obj?.a.b of []); +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any +>[] : never[] + +({ a: obj?.a } = { a: 1 }); +>({ a: obj?.a } = { a: 1 }) : { a: number; } +>{ a: obj?.a } = { a: 1 } : { a: number; } +>{ a: obj?.a } : { a: any; } +>a : any +>obj?.a : any +>obj : any +>a : any +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +({ a: obj?.a.b } = { a: 1 }); +>({ a: obj?.a.b } = { a: 1 }) : { a: number; } +>{ a: obj?.a.b } = { a: 1 } : { a: number; } +>{ a: obj?.a.b } : { a: any; } +>a : any +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +({ ...obj?.a } = { a: 1 }); +>({ ...obj?.a } = { a: 1 }) : { a: number; } +>{ ...obj?.a } = { a: 1 } : { a: number; } +>{ ...obj?.a } : any +>obj?.a : any +>obj : any +>a : any +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +({ ...obj?.a.b } = { a: 1 }); +>({ ...obj?.a.b } = { a: 1 }) : { a: number; } +>{ ...obj?.a.b } = { a: 1 } : { a: number; } +>{ ...obj?.a.b } : any +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any +>{ a: 1 } : { a: number; } +>a : number +>1 : 1 + +[...obj?.a] = []; +>[...obj?.a] = [] : never[] +>[...obj?.a] : never[] +>...obj?.a : any +>obj?.a : any +>obj : any +>a : any +>[] : never[] + +[...obj?.a.b] = []; +>[...obj?.a.b] = [] : never[] +>[...obj?.a.b] : never[] +>...obj?.a.b : any +>obj?.a.b : any +>obj?.a : any +>obj : any +>a : any +>b : any +>[] : never[] + diff --git a/tests/baselines/reference/propertyAccessChain.js b/tests/baselines/reference/propertyAccessChain.js new file mode 100644 index 0000000000000..0c15856b15cc1 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.js @@ -0,0 +1,25 @@ +//// [propertyAccessChain.ts] +declare const o1: undefined | { b: string }; +o1?.b; + +declare const o2: undefined | { b: { c: string } }; +o2?.b.c; + +declare const o3: { b: undefined | { c: string } }; +o3.b?.c; + +declare const o4: { b?: { c: { d?: { e: string } } } }; +o4.b?.c.d?.e; + +declare const o5: { b?(): { c: { d?: { e: string } } } }; +o5.b?.().c.d?.e; + + +//// [propertyAccessChain.js] +"use strict"; +var _a, _b, _c, _d, _e, _f, _g, _h; +(_a = o1) === null || _a === void 0 ? void 0 : _a.b; +(_b = o2) === null || _b === void 0 ? void 0 : _b.b.c; +(_c = o3.b) === null || _c === void 0 ? void 0 : _c.c; +(_e = (_d = o4.b) === null || _d === void 0 ? void 0 : _d.c.d) === null || _e === void 0 ? void 0 : _e.e; +(_h = (_g = (_f = o5).b) === null || _g === void 0 ? void 0 : _g.call(_f).c.d) === null || _h === void 0 ? void 0 : _h.e; diff --git a/tests/baselines/reference/propertyAccessChain.symbols b/tests/baselines/reference/propertyAccessChain.symbols new file mode 100644 index 0000000000000..8b5cea9fe67a0 --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.symbols @@ -0,0 +1,70 @@ +=== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.ts === +declare const o1: undefined | { b: string }; +>o1 : Symbol(o1, Decl(propertyAccessChain.ts, 0, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 0, 31)) + +o1?.b; +>o1?.b : Symbol(b, Decl(propertyAccessChain.ts, 0, 31)) +>o1 : Symbol(o1, Decl(propertyAccessChain.ts, 0, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 0, 31)) + +declare const o2: undefined | { b: { c: string } }; +>o2 : Symbol(o2, Decl(propertyAccessChain.ts, 3, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 3, 31)) +>c : Symbol(c, Decl(propertyAccessChain.ts, 3, 36)) + +o2?.b.c; +>o2?.b.c : Symbol(c, Decl(propertyAccessChain.ts, 3, 36)) +>o2?.b : Symbol(b, Decl(propertyAccessChain.ts, 3, 31)) +>o2 : Symbol(o2, Decl(propertyAccessChain.ts, 3, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 3, 31)) +>c : Symbol(c, Decl(propertyAccessChain.ts, 3, 36)) + +declare const o3: { b: undefined | { c: string } }; +>o3 : Symbol(o3, Decl(propertyAccessChain.ts, 6, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 6, 19)) +>c : Symbol(c, Decl(propertyAccessChain.ts, 6, 36)) + +o3.b?.c; +>o3.b?.c : Symbol(c, Decl(propertyAccessChain.ts, 6, 36)) +>o3.b : Symbol(b, Decl(propertyAccessChain.ts, 6, 19)) +>o3 : Symbol(o3, Decl(propertyAccessChain.ts, 6, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 6, 19)) +>c : Symbol(c, Decl(propertyAccessChain.ts, 6, 36)) + +declare const o4: { b?: { c: { d?: { e: string } } } }; +>o4 : Symbol(o4, Decl(propertyAccessChain.ts, 9, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 9, 19)) +>c : Symbol(c, Decl(propertyAccessChain.ts, 9, 25)) +>d : Symbol(d, Decl(propertyAccessChain.ts, 9, 30)) +>e : Symbol(e, Decl(propertyAccessChain.ts, 9, 36)) + +o4.b?.c.d?.e; +>o4.b?.c.d?.e : Symbol(e, Decl(propertyAccessChain.ts, 9, 36)) +>o4.b?.c.d : Symbol(d, Decl(propertyAccessChain.ts, 9, 30)) +>o4.b?.c : Symbol(c, Decl(propertyAccessChain.ts, 9, 25)) +>o4.b : Symbol(b, Decl(propertyAccessChain.ts, 9, 19)) +>o4 : Symbol(o4, Decl(propertyAccessChain.ts, 9, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 9, 19)) +>c : Symbol(c, Decl(propertyAccessChain.ts, 9, 25)) +>d : Symbol(d, Decl(propertyAccessChain.ts, 9, 30)) +>e : Symbol(e, Decl(propertyAccessChain.ts, 9, 36)) + +declare const o5: { b?(): { c: { d?: { e: string } } } }; +>o5 : Symbol(o5, Decl(propertyAccessChain.ts, 12, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 12, 19)) +>c : Symbol(c, Decl(propertyAccessChain.ts, 12, 27)) +>d : Symbol(d, Decl(propertyAccessChain.ts, 12, 32)) +>e : Symbol(e, Decl(propertyAccessChain.ts, 12, 38)) + +o5.b?.().c.d?.e; +>o5.b?.().c.d?.e : Symbol(e, Decl(propertyAccessChain.ts, 12, 38)) +>o5.b?.().c.d : Symbol(d, Decl(propertyAccessChain.ts, 12, 32)) +>o5.b?.().c : Symbol(c, Decl(propertyAccessChain.ts, 12, 27)) +>o5.b : Symbol(b, Decl(propertyAccessChain.ts, 12, 19)) +>o5 : Symbol(o5, Decl(propertyAccessChain.ts, 12, 13)) +>b : Symbol(b, Decl(propertyAccessChain.ts, 12, 19)) +>c : Symbol(c, Decl(propertyAccessChain.ts, 12, 27)) +>d : Symbol(d, Decl(propertyAccessChain.ts, 12, 32)) +>e : Symbol(e, Decl(propertyAccessChain.ts, 12, 38)) + diff --git a/tests/baselines/reference/propertyAccessChain.types b/tests/baselines/reference/propertyAccessChain.types new file mode 100644 index 0000000000000..2cb030ac3f66c --- /dev/null +++ b/tests/baselines/reference/propertyAccessChain.types @@ -0,0 +1,71 @@ +=== tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.ts === +declare const o1: undefined | { b: string }; +>o1 : { b: string; } | undefined +>b : string + +o1?.b; +>o1?.b : string | undefined +>o1 : { b: string; } | undefined +>b : string | undefined + +declare const o2: undefined | { b: { c: string } }; +>o2 : { b: { c: string; }; } | undefined +>b : { c: string; } +>c : string + +o2?.b.c; +>o2?.b.c : string | undefined +>o2?.b : { c: string; } | undefined +>o2 : { b: { c: string; }; } | undefined +>b : { c: string; } | undefined +>c : string | undefined + +declare const o3: { b: undefined | { c: string } }; +>o3 : { b: { c: string; } | undefined; } +>b : { c: string; } | undefined +>c : string + +o3.b?.c; +>o3.b?.c : string | undefined +>o3.b : { c: string; } | undefined +>o3 : { b: { c: string; } | undefined; } +>b : { c: string; } | undefined +>c : string | undefined + +declare const o4: { b?: { c: { d?: { e: string } } } }; +>o4 : { b?: { c: { d?: { e: string; } | undefined; }; } | undefined; } +>b : { c: { d?: { e: string; } | undefined; }; } | undefined +>c : { d?: { e: string; } | undefined; } +>d : { e: string; } | undefined +>e : string + +o4.b?.c.d?.e; +>o4.b?.c.d?.e : string | undefined +>o4.b?.c.d : { e: string; } | undefined +>o4.b?.c : { d?: { e: string; } | undefined; } | undefined +>o4.b : { c: { d?: { e: string; } | undefined; }; } | undefined +>o4 : { b?: { c: { d?: { e: string; } | undefined; }; } | undefined; } +>b : { c: { d?: { e: string; } | undefined; }; } | undefined +>c : { d?: { e: string; } | undefined; } | undefined +>d : { e: string; } | undefined +>e : string | undefined + +declare const o5: { b?(): { c: { d?: { e: string } } } }; +>o5 : { b?(): { c: { d?: { e: string; } | undefined; }; }; } +>b : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>c : { d?: { e: string; } | undefined; } +>d : { e: string; } | undefined +>e : string + +o5.b?.().c.d?.e; +>o5.b?.().c.d?.e : string | undefined +>o5.b?.().c.d : { e: string; } | undefined +>o5.b?.().c : { d?: { e: string; } | undefined; } | undefined +>o5.b?.() : { c: { d?: { e: string; } | undefined; }; } | undefined +>o5.b : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>o5 : { b?(): { c: { d?: { e: string; } | undefined; }; }; } +>b : (() => { c: { d?: { e: string; } | undefined; }; }) | undefined +>c : { d?: { e: string; } | undefined; } | undefined +>d : { e: string; } | undefined +>e : string | undefined + diff --git a/tests/baselines/reference/taggedTemplateChain.errors.txt b/tests/baselines/reference/taggedTemplateChain.errors.txt new file mode 100644 index 0000000000000..c38f1d014ae4c --- /dev/null +++ b/tests/baselines/reference/taggedTemplateChain.errors.txt @@ -0,0 +1,13 @@ +tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts(2,4): error TS1358: Tagged template expressions are not permitted in an optional chain. +tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts(4,4): error TS1358: Tagged template expressions are not permitted in an optional chain. + + +==== tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts (2 errors) ==== + declare let a: any; + a?.`b`; + ~~~ +!!! error TS1358: Tagged template expressions are not permitted in an optional chain. + + a?.`b${1}c`; + ~~~~~~~~ +!!! error TS1358: Tagged template expressions are not permitted in an optional chain. \ No newline at end of file diff --git a/tests/baselines/reference/taggedTemplateChain.js b/tests/baselines/reference/taggedTemplateChain.js new file mode 100644 index 0000000000000..c96237185fb45 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateChain.js @@ -0,0 +1,13 @@ +//// [taggedTemplateChain.ts] +declare let a: any; +a?.`b`; + +a?.`b${1}c`; + +//// [taggedTemplateChain.js] +var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { + if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } + return cooked; +}; +a(__makeTemplateObject(["b"], ["b"])); +a(__makeTemplateObject(["b", "c"], ["b", "c"]), 1); diff --git a/tests/baselines/reference/taggedTemplateChain.symbols b/tests/baselines/reference/taggedTemplateChain.symbols new file mode 100644 index 0000000000000..4dde630cab6ac --- /dev/null +++ b/tests/baselines/reference/taggedTemplateChain.symbols @@ -0,0 +1,10 @@ +=== tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts === +declare let a: any; +>a : Symbol(a, Decl(taggedTemplateChain.ts, 0, 11)) + +a?.`b`; +>a : Symbol(a, Decl(taggedTemplateChain.ts, 0, 11)) + +a?.`b${1}c`; +>a : Symbol(a, Decl(taggedTemplateChain.ts, 0, 11)) + diff --git a/tests/baselines/reference/taggedTemplateChain.types b/tests/baselines/reference/taggedTemplateChain.types new file mode 100644 index 0000000000000..9690fc22f6991 --- /dev/null +++ b/tests/baselines/reference/taggedTemplateChain.types @@ -0,0 +1,15 @@ +=== tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts === +declare let a: any; +>a : any + +a?.`b`; +>a?.`b` : any +>a : any +>`b` : "b" + +a?.`b${1}c`; +>a?.`b${1}c` : any +>a : any +>`b${1}c` : string +>1 : 1 + diff --git a/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts new file mode 100644 index 0000000000000..dadb1b36076db --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts @@ -0,0 +1,163 @@ +// @strict: true +// @allowUnreachableCode: false + +// assignments in shortcutting chain +declare const o: undefined | { + [key: string]: any; + [key: number]: any; + (...args: any[]): any; +}; + +let a: number; +o?.[a = 1]; +a.toString(); + +let b: number; +o?.x[b = 1]; +b.toString(); + +let c: number; +o?.(c = 1) +c.toString(); + +let d: number; +o?.x(d = 1); +d.toString(); + +// type predicates +declare const f: undefined | ((x: any) => x is number); +declare const x: string | number; +if (f?.(x)) { + x; // number + f; // (x: any) => x is number + f(x); +} +else { + x; + f; + f(x); +} +x; +f; +f(x); + +declare const o2: { f(x: any): x is number; } | undefined; +if (o2?.f(x)) { + x; // number + o2.f; // (x: any) => x is number + o2?.f; + o2?.f(x); +} +else { + x; + o2; + o2?.f; + o2.f; +} +x; +o2; +o2?.f; +o2.f; + +declare const o3: { x: 1, y: string } | { x: 2, y: number } | undefined; +if (o3?.x === 1) { + o3; // TODO: should be `{ x: y, y: string }` + o3.x; // TODO: should not be an error. + o3?.x; +} +else { + o3; + o3?.x; + o3.x; +} +o3; +o3?.x; +o3.x; + +declare const o4: { x?: { y: boolean } }; +if (o4.x?.y) { + o4.x; // { y: boolean } + o4.x.y; // true + o4.x?.y; // true +} +else { + o4.x; + o4.x?.y; + o4.x.y; +} +o4.x; +o4.x?.y; +o4.x.y; + +declare const o5: { x?: { y: { z?: { w: boolean } } } }; +if (o5.x?.y.z?.w) { + o5.x; + o5.x.y; + o5.x.y.z; + o5.x.y.z.w; // true + o5.x.y.z?.w; // true + o5.x?.y.z.w; // true + o5.x?.y.z?.w; // true +} +else { + o5.x; + o5.x?.y; + o5.x?.y.z; + o5.x?.y.z?.w; + o5.x.y; + o5.x.y.z.w; +} +o5.x; +o5.x?.y; +o5.x?.y.z; +o5.x?.y.z?.w; +o5.x.y; +o5.x.y.z.w; + +interface Base { + f(): this is Derived; +} + +interface Derived extends Base { + x: number; +} + +declare const o6: Base | undefined; +if (o6?.f()) { + o6; // Derived + o6.f; +} +else { + o6; + o6?.f; + o6.f; +} +o6; +o6?.f; +o6.f; + +// asserts +declare const isDefined: (value: T) => asserts value is NonNullable; +declare const isString: (value: unknown) => asserts value is string; +declare const maybeIsString: undefined | ((value: unknown) => asserts value is string); +declare const maybeNever: undefined | (() => never); + +function f01(x: unknown) { + if (!!true) { + isString?.(x); + x; + } + if (!!true) { + maybeIsString?.(x); + x; + } + if (!!true) { + isDefined(maybeIsString); + maybeIsString?.(x); + x; + } + if (!!true) { + maybeNever?.(); + x; + } +} diff --git a/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts b/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts new file mode 100644 index 0000000000000..58e51858da6a1 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.2.ts @@ -0,0 +1,10 @@ +// @strict: false + +declare const o1: undefined | (() => number); +o1?.(); + +declare const o2: undefined | { b: () => number }; +o2?.b(); + +declare const o3: { b: (() => { c: string }) | undefined }; +o3.b?.().c; diff --git a/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.3.ts b/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.3.ts new file mode 100644 index 0000000000000..ee0d8078e730e --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.3.ts @@ -0,0 +1,12 @@ +// @strict: true + +declare function absorb(): T; +declare const a: { m?(obj: {x: T}): T } | undefined; +const n1: number = a?.m?.({x: 12 }); // should be an error (`undefined` is not assignable to `number`) +const n2: number = a?.m?.({x: absorb()}); // likewise +const n3: number | undefined = a?.m?.({x: 12}); // should be ok +const n4: number | undefined = a?.m?.({x: absorb()}); // likewise + +// Also a test showing `!` vs `?` for good measure +let t1 = a?.m?.({x: 12}); +t1 = a!.m!({x: 12}); \ No newline at end of file diff --git a/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.ts b/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.ts new file mode 100644 index 0000000000000..fa24d920a1918 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/callChain/callChain.ts @@ -0,0 +1,35 @@ +// @strict: true + +declare const o1: undefined | ((...args: any[]) => number); +o1?.(); +o1?.(1); +o1?.(...[1, 2]); +o1?.(1, ...[2, 3], 4); + +declare const o2: undefined | { b: (...args: any[]) => number }; +o2?.b(); +o2?.b(1); +o2?.b(...[1, 2]); +o2?.b(1, ...[2, 3], 4); +o2?.["b"](); +o2?.["b"](1); +o2?.["b"](...[1, 2]); +o2?.["b"](1, ...[2, 3], 4); + +declare const o3: { b: ((...args: any[]) => { c: string }) | undefined }; +o3.b?.().c; +o3.b?.(1).c; +o3.b?.(...[1, 2]).c; +o3.b?.(1, ...[2, 3], 4).c; +o3.b?.()["c"]; +o3.b?.(1)["c"]; +o3.b?.(...[1, 2])["c"]; +o3.b?.(1, ...[2, 3], 4)["c"]; +o3["b"]?.().c; +o3["b"]?.(1).c; +o3["b"]?.(...[1, 2]).c; +o3["b"]?.(1, ...[2, 3], 4).c; + +declare const o4: undefined | ((f: (a: T) => T) => T); +declare function incr(x: number): number; +const v: number | undefined = o4?.(incr); \ No newline at end of file diff --git a/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts b/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts new file mode 100644 index 0000000000000..00834b4d968a0 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.2.ts @@ -0,0 +1,12 @@ +// @strict: false + +declare const o1: undefined | { b: string }; +o1?.["b"]; + +declare const o2: undefined | { b: { c: string } }; +o2?.["b"].c; +o2?.b["c"]; + +declare const o3: { b: undefined | { c: string } }; +o3["b"]?.c; +o3.b?.["c"]; diff --git a/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts b/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts new file mode 100644 index 0000000000000..b494ac968e9be --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts @@ -0,0 +1,30 @@ +// @strict: true + +declare const obj: any; + +obj?.["a"]++; +obj?.a["b"]++; +obj?.["a"]--; +obj?.a["b"]--; + +++obj?.["a"]; +++obj?.a["b"]; +--obj?.["a"]; +--obj?.a["b"]; + +obj?.["a"] = 1; +obj?.a["b"] = 1; +obj?.["a"] += 1; +obj?.a["b"] += 1; + +for (obj?.["a"] in {}); +for (obj?.a["b"] in {}); +for (obj?.["a"] of []); +for (obj?.a["b"] of []); + +({ a: obj?.["a"] } = { a: 1 }); +({ a: obj?.a["b"] } = { a: 1 }); +({ ...obj?.["a"] } = { a: 1 }); +({ ...obj?.a["b"] } = { a: 1 }); +[...obj?.["a"]] = []; +[...obj?.a["b"]] = []; diff --git a/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.ts b/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.ts new file mode 100644 index 0000000000000..6718c050a036a --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.ts @@ -0,0 +1,22 @@ +// @strict: true + +declare const o1: undefined | { b: string }; +o1?.["b"]; + +declare const o2: undefined | { b: { c: string } }; +o2?.["b"].c; +o2?.b["c"]; + +declare const o3: { b: undefined | { c: string } }; +o3["b"]?.c; +o3.b?.["c"]; + +declare const o4: { b?: { c: { d?: { e: string } } } }; +o4.b?.["c"].d?.e; +o4.b?.["c"].d?.["e"]; + +declare const o5: { b?(): { c: { d?: { e: string } } } }; +o5.b?.()["c"].d?.e; +o5.b?.()["c"].d?.["e"]; +o5["b"]?.()["c"].d?.e; +o5["b"]?.()["c"].d?.["e"]; diff --git a/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts b/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts new file mode 100644 index 0000000000000..93e37f0b48f64 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.2.ts @@ -0,0 +1,10 @@ +// @strict: false + +declare const o1: undefined | { b: string }; +o1?.b; + +declare const o2: undefined | { b: { c: string } }; +o2?.b.c; + +declare const o3: { b: undefined | { c: string } }; +o3.b?.c; diff --git a/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts b/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts new file mode 100644 index 0000000000000..82c96bbf82f1f --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.3.ts @@ -0,0 +1,30 @@ +// @strict: true + +declare const obj: any; + +obj?.a++; +obj?.a.b++; +obj?.a--; +obj?.a.b--; + +++obj?.a; +++obj?.a.b; +--obj?.a; +--obj?.a.b; + +obj?.a = 1; +obj?.a.b = 1; +obj?.a += 1; +obj?.a.b += 1; + +for (obj?.a in {}); +for (obj?.a.b in {}); +for (obj?.a of []); +for (obj?.a.b of []); + +({ a: obj?.a } = { a: 1 }); +({ a: obj?.a.b } = { a: 1 }); +({ ...obj?.a } = { a: 1 }); +({ ...obj?.a.b } = { a: 1 }); +[...obj?.a] = []; +[...obj?.a.b] = []; diff --git a/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.ts b/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.ts new file mode 100644 index 0000000000000..e76d95b454017 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/propertyAccessChain/propertyAccessChain.ts @@ -0,0 +1,16 @@ +// @strict: true + +declare const o1: undefined | { b: string }; +o1?.b; + +declare const o2: undefined | { b: { c: string } }; +o2?.b.c; + +declare const o3: { b: undefined | { c: string } }; +o3.b?.c; + +declare const o4: { b?: { c: { d?: { e: string } } } }; +o4.b?.c.d?.e; + +declare const o5: { b?(): { c: { d?: { e: string } } } }; +o5.b?.().c.d?.e; diff --git a/tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts b/tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts new file mode 100644 index 0000000000000..6a3bb04336e63 --- /dev/null +++ b/tests/cases/conformance/expressions/optionalChaining/taggedTemplateChain/taggedTemplateChain.ts @@ -0,0 +1,4 @@ +declare let a: any; +a?.`b`; + +a?.`b${1}c`; \ No newline at end of file diff --git a/tests/cases/fourslash/completionAfterQuestionDot.ts b/tests/cases/fourslash/completionAfterQuestionDot.ts new file mode 100644 index 0000000000000..7d31e775a185a --- /dev/null +++ b/tests/cases/fourslash/completionAfterQuestionDot.ts @@ -0,0 +1,20 @@ +/// +// @strict: true + +//// interface User { +//// address?: { +//// city: string; +//// "postal code": string; +//// } +//// }; +//// declare const user: User; +//// user.address[|?./**/|] + +verify.completions({ + marker: "", + exact: [ + { name: "city", text: "(property) city: string" }, + { name: "postal code", text: "(property) \"postal code\": string", insertText: "?.[\"postal code\"]", replacementSpan: test.ranges()[0] } + ], + preferences: { includeInsertTextCompletions: true }, +}); diff --git a/tests/cases/fourslash/completionAutoInsertQuestionDot.ts b/tests/cases/fourslash/completionAutoInsertQuestionDot.ts new file mode 100644 index 0000000000000..e1998007c76a6 --- /dev/null +++ b/tests/cases/fourslash/completionAutoInsertQuestionDot.ts @@ -0,0 +1,20 @@ +/// +// @strict: true + +//// interface User { +//// address?: { +//// city: string; +//// "postal code": string; +//// } +//// }; +//// declare const user: User; +//// user.address[|./**/|] + +verify.completions({ + marker: "", + exact: [ + { name: "city", text: "(property) city: string", insertText: "?.city", replacementSpan: test.ranges()[0] }, + { name: "postal code", text: "(property) \"postal code\": string", insertText: "?.[\"postal code\"]", replacementSpan: test.ranges()[0] } + ], + preferences: { includeInsertTextCompletions: true }, +}); diff --git a/tests/cases/fourslash/completionNoAutoInsertQuestionDotForThis.ts b/tests/cases/fourslash/completionNoAutoInsertQuestionDotForThis.ts new file mode 100644 index 0000000000000..4a99155bc7944 --- /dev/null +++ b/tests/cases/fourslash/completionNoAutoInsertQuestionDotForThis.ts @@ -0,0 +1,20 @@ +/// +// @strict: true + +//// class Address { +//// city: string = ""; +//// "postal code": string = ""; +//// method() { +//// this[|./**/|] +//// } +//// } + +verify.completions({ + marker: "", + exact: [ + { name: "city", text: "(property) Address.city: string", insertText: undefined }, + { name: "postal code", text: "(property) Address[\"postal code\"]: string", insertText: "[\"postal code\"]", replacementSpan: test.ranges()[0] }, + { name: "method" } + ], + preferences: { includeInsertTextCompletions: true }, +}); diff --git a/tests/cases/fourslash/completionNoAutoInsertQuestionDotForTypeParameter.ts b/tests/cases/fourslash/completionNoAutoInsertQuestionDotForTypeParameter.ts new file mode 100644 index 0000000000000..6061ef37df21a --- /dev/null +++ b/tests/cases/fourslash/completionNoAutoInsertQuestionDotForTypeParameter.ts @@ -0,0 +1,19 @@ +/// +// @strict: true + +//// interface Address { +//// city: string = ""; +//// "postal code": string = ""; +//// } +//// function f(x: T) { +//// x[|./**/|] +//// } + +verify.completions({ + marker: "", + exact: [ + { name: "city", text: "(property) Address.city: string" }, + { name: "postal code", text: "(property) Address[\"postal code\"]: string", insertText: "[\"postal code\"]", replacementSpan: test.ranges()[0] } + ], + preferences: { includeInsertTextCompletions: true }, +}); diff --git a/tests/cases/fourslash/signatureHelpOptionalCall.ts b/tests/cases/fourslash/signatureHelpOptionalCall.ts new file mode 100644 index 0000000000000..fea90d7ee1f2a --- /dev/null +++ b/tests/cases/fourslash/signatureHelpOptionalCall.ts @@ -0,0 +1,14 @@ +/// + +////function fnTest(str: string, num: number) { } +////fnTest?.(/*1*/); + +verify.signatureHelp( + { + marker: "1", + text: 'fnTest(str: string, num: number): void', + parameterCount: 2, + parameterName: "str", + parameterSpan: "str: string", + }, +); diff --git a/tests/cases/fourslash/signatureHelpOptionalCall2.ts b/tests/cases/fourslash/signatureHelpOptionalCall2.ts new file mode 100644 index 0000000000000..ab388398177a0 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpOptionalCall2.ts @@ -0,0 +1,14 @@ +/// + +////declare const fnTest: undefined | ((str: string, num: number) => void); +////fnTest?.(/*1*/); + +verify.signatureHelp( + { + marker: "1", + text: 'fnTest(str: string, num: number): void', + parameterCount: 2, + parameterName: "str", + parameterSpan: "str: string", + }, +);