diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4baec35bfbd..2c1c6fb11aa 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -865,7 +865,8 @@ namespace ts { else { return node.kind === SyntaxKind.BinaryExpression && ( (node).operatorToken.kind === SyntaxKind.AmpersandAmpersandToken || - (node).operatorToken.kind === SyntaxKind.BarBarToken); + (node).operatorToken.kind === SyntaxKind.BarBarToken || + (node).operatorToken.kind === SyntaxKind.QuestionQuestionToken); } } } @@ -1230,7 +1231,9 @@ namespace ts { function bindBinaryExpressionFlow(node: BinaryExpression) { const operator = node.operatorToken.kind; - if (operator === SyntaxKind.AmpersandAmpersandToken || operator === SyntaxKind.BarBarToken) { + if (operator === SyntaxKind.AmpersandAmpersandToken || + operator === SyntaxKind.BarBarToken || + operator === SyntaxKind.QuestionQuestionToken) { if (isTopLevelLogicalExpression(node)) { const postExpressionLabel = createBranchLabel(); bindLogicalExpression(node, postExpressionLabel, postExpressionLabel); @@ -2546,6 +2549,10 @@ namespace ts { transformFlags |= TransformFlags.AssertTypeScript; } + if (node.flags & NodeFlags.PropagateNull) { + transformFlags |= TransformFlags.AssertESNext; + } + if (subtreeFlags & TransformFlags.ContainsSpread || isSuperOrSuperProperty(expression, expressionKind)) { // If the this node contains a SpreadExpression, or is a super call, then it is an ES6 @@ -2577,6 +2584,9 @@ namespace ts { if (node.typeArguments) { transformFlags |= TransformFlags.AssertTypeScript; } + if (node.flags & NodeFlags.PropagateNull) { + transformFlags |= TransformFlags.AssertESNext; + } if (subtreeFlags & TransformFlags.ContainsSpread) { // If the this node contains a SpreadElementExpression then it is an ES6 // node. @@ -2987,6 +2997,9 @@ namespace ts { let transformFlags = subtreeFlags; const expression = node.expression; const expressionKind = expression.kind; + if (node.flags & NodeFlags.PropagateNull) { + transformFlags |= TransformFlags.AssertESNext; + } // If a PropertyAccessExpression starts with a super keyword, then it is // ES6 syntax, and requires a lexical `this` binding. @@ -3111,6 +3124,7 @@ namespace ts { switch (kind) { case SyntaxKind.BarGreaterThanToken: + case SyntaxKind.QuestionQuestionToken: transformFlags |= TransformFlags.AssertESNext; break; @@ -3315,6 +3329,12 @@ namespace ts { break; + case SyntaxKind.ElementAccessExpression: + if (node.flags & NodeFlags.PropagateNull) { + transformFlags |= TransformFlags.AssertESNext; + } + break; + case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: case SyntaxKind.ForStatement: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 774d78ae148..b7a0d29018e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -163,7 +163,6 @@ namespace ts { const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const silentNeverSignature = createSignature(undefined, undefined, undefined, emptyArray, silentNeverType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); - const resolvingPartialSignatures: Signature[] = []; const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); const operatorExpressionTypes = createMap(); @@ -2618,7 +2617,7 @@ namespace ts { function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { const parameterNode = p.valueDeclaration; - if ((isTransient(p) && p.transientSymbolIsRest) || isRestParameter(parameterNode)) { + if (isTransient(p) && typeof p.transientSymbolIsRest === "boolean" ? p.transientSymbolIsRest : isRestParameter(parameterNode)) { writePunctuation(writer, SyntaxKind.DotDotDotToken); } if (parameterNode && isBindingPattern(parameterNode.name)) { @@ -8170,6 +8169,20 @@ namespace ts { return strictNullChecks ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type; } + function propagateNullType(type: Type, propagatingType: Type) { + return propagatingType === neverType ? type : getUnionType([type, propagatingType]); + } + + function propagateNullReturnType(signature: Signature, propagatingType: Type) { + if (propagatingType === neverType || isTypeAny(signature.resolvedReturnType)) { + return signature; + } + + signature = cloneSignature(signature); + signature.resolvedReturnType = getUnionType([signature.resolvedReturnType, propagatingType]); + return signature; + } + /** * Return true if type was inferred from an object literal or written as an object type literal * with no call or construct signatures. @@ -11122,6 +11135,14 @@ namespace ts { // with this type. It is neither affected by it, nor does it propagate it to its operand. // So the fact that contextualMapper is passed is not important, because the operand of a spread // element is not contextually typed. + if (node.expression.kind === SyntaxKind.OmittedExpression && ( + node.parent.kind === SyntaxKind.CallExpression || + node.parent.kind === SyntaxKind.NewExpression)) { + // positional spread in an argument list indicates partial application. + // we indicate `unknownType` here as the type is not known and should not + // introduce type errors. + return unknownType; + } const arrayOrIterableType = checkExpressionCached(node.expression, contextualMapper); return checkIteratedTypeOrElementType(arrayOrIterableType, node.expression, /*allowStringInput*/ false); } @@ -12070,7 +12091,11 @@ namespace ts { } function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) { - const type = checkNonNullExpression(left); + const propagateNull = node.flags & NodeFlags.PropagateNull; + const objectType = propagateNull ? checkExpression(left) : checkNonNullExpression(left); + const type = propagateNull ? getNonNullableType(objectType) : objectType; + const propagatingType = propagateNull ? getTypeWithFacts(objectType, TypeFacts.EQUndefinedOrNull) : neverType; + if (isTypeAny(type) || type === silentNeverType) { return type; } @@ -12112,10 +12137,10 @@ namespace ts { if (node.kind !== SyntaxKind.PropertyAccessExpression || assignmentKind === AssignmentKind.Definite || !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) { - return propType; + return propagateNullType(propType, propagatingType); } const flowType = getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined); - return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType; + return propagateNullType(assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType, propagatingType); } function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean { @@ -12184,7 +12209,10 @@ namespace ts { } function checkIndexedAccess(node: ElementAccessExpression): Type { - const objectType = checkNonNullExpression(node.expression); + const propagateNull = node.flags & NodeFlags.PropagateNull; + const objectType = propagateNull ? checkExpression(node.expression) : checkNonNullExpression(node.expression); + const type = propagateNull ? getNonNullableType(objectType) : objectType; + const propagatingType = propagateNull ? getTypeWithFacts(objectType, TypeFacts.EQUndefinedOrNull) : neverType; const indexExpression = node.argumentExpression; if (!indexExpression) { @@ -12204,27 +12232,24 @@ namespace ts { const indexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : checkExpression(indexExpression); - if (objectType === unknownType || objectType === silentNeverType) { - return objectType; + if (type === unknownType || type === silentNeverType) { + return type; } - if (isConstEnumObjectType(objectType) && indexExpression.kind !== SyntaxKind.StringLiteral) { + if (isConstEnumObjectType(type) && indexExpression.kind !== SyntaxKind.StringLiteral) { error(indexExpression, Diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal); return unknownType; } - return getIndexedAccessType(objectType, indexType, node); + return propagateNullType(getIndexedAccessType(type, indexType, node), propagatingType); } function checkBindExpression(node: BindExpression | BindToExpression): Type { - if (node.kind === SyntaxKind.BindToExpression) { - checkNonNullExpression(node.targetExpression); + const signature = getResolvedSignature(node); + if (signature === unknownSignature) { + return unknownType; } - const signatures = getResolvedPartialSignatures(node); - if (signatures.length === 0 || singleOrUndefined(signatures) === unknownSignature) { - return unknownType - } - return createTypeFromSignatures(map(signatures, getSignatureWithoutThis)); + return getReturnTypeOfSignature(signature); } function checkThatExpressionIsProperSymbolReference(expression: Expression, expressionType: Type, reportError: boolean): boolean { @@ -12269,7 +12294,7 @@ namespace ts { return true; } - function resolveUntypedCall(node: CallLikeExpression, partialSignaturesOutArray: Signature[]): Signature { + function resolveUntypedCall(node: CallLikeExpression): Signature { if (node.kind === SyntaxKind.TaggedTemplateExpression) { checkExpression((node).template); } @@ -12278,17 +12303,11 @@ namespace ts { checkExpression(argument); }); } - if (partialSignaturesOutArray) { - partialSignaturesOutArray.push(anySignature); - } return anySignature; } - function resolveErrorCall(node: CallLikeExpression, partialSignaturesOutArray: Signature[]): Signature { - resolveUntypedCall(node, /*partialSignaturesOutArray*/ undefined); - if (partialSignaturesOutArray) { - partialSignaturesOutArray.push(unknownSignature); - } + function resolveErrorCall(node: CallLikeExpression): Signature { + resolveUntypedCall(node); return unknownSignature; } @@ -12655,20 +12674,57 @@ namespace ts { return undefined; } else if (node.kind === SyntaxKind.BinaryExpression) { - let expression = (node).left; + const expression = (node).left; + const pipelineArguments: Expression[] = []; if (expression.kind === SyntaxKind.ParenthesizedExpression) { - // comma expressions are right-deep - args = []; - expression = (expression).expression; - while (expression.kind === SyntaxKind.BinaryExpression && - (expression).operatorToken.kind === SyntaxKind.CommaToken) { - args.push((expression).left); - expression = (expression).right; - } - args.push(expression); + collectPipelineArguments((expression).expression, pipelineArguments); } else { - args = [expression]; + pipelineArguments.push(expression); + } + + if (isPartialApplication(node.right)) { + const argumentList = node.right.arguments; + const positionalOffsetMap = new Array(argumentList.length); + let numFreePositions = 0; + let maxFixedPosition = -1; + for (let i = 0; i < argumentList.length; i++) { + const argument = argumentList[i]; + if (isPositionalElement(argument)) { + if (argument.literal) { + const fixedPosition = +argument.literal.text | 0; + if (maxFixedPosition < fixedPosition) { + maxFixedPosition = fixedPosition; + } + positionalOffsetMap[i] = fixedPosition; + } + else { + positionalOffsetMap[i] = numFreePositions; + numFreePositions++; + } + } + } + + args = []; + + const maxNonRestPosition = maxFixedPosition >= numFreePositions ? maxFixedPosition + 1 : numFreePositions; + for (let i = 0; i < argumentList.length; i++) { + const argument = argumentList[i]; + if (isPositionalElement(argument)) { + args.push(pipelineArguments[positionalOffsetMap[i]] || argument); + } + else if (isPositionalSpreadElement(argument)) { + for (let j = maxNonRestPosition; j < pipelineArguments.length; j++) { + args.push(pipelineArguments[j]); + } + } + else { + args.push(argument); + } + } + } + else { + args = pipelineArguments; } } else { @@ -12678,6 +12734,15 @@ namespace ts { return args; } + function collectPipelineArguments(node: Expression, argumentList: Expression[]) { + if (isCommaExpression(node)) { + collectPipelineArguments(node.left, argumentList); + collectPipelineArguments(node.right, argumentList); + } + else { + argumentList.push(node); + } + } /** * Returns the effective argument count for a node that works like a function invocation. @@ -12939,7 +13004,6 @@ namespace ts { (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression)) { return undefined; } - return args[argIndex]; } @@ -12960,14 +13024,28 @@ namespace ts { } } - function resolveCall(node: CallLikeExpression, signatures: Signature[], partialSignaturesOutArray: Signature[], candidatesOutArray: Signature[], headMessage?: DiagnosticMessage): Signature { - let typeArguments: TypeNode[]; + function getEffectiveTypeArguments(node: CallLikeExpression): NodeArray { if (node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression) { - typeArguments = node.typeArguments; + const typeArguments = node.typeArguments; // We already perform checking on the type arguments on the class declaration itself. if ((node).expression.kind !== SyntaxKind.SuperKeyword) { forEach(typeArguments, checkSourceElement); } + return typeArguments; + } + if (node.kind === SyntaxKind.BinaryExpression && isPartialApplication(node.right)) { + return getEffectiveTypeArguments(node.right); + } + } + + function resolveCall(node: CallLikeExpression, signatures: Signature[], candidatesOutArray: Signature[], propagatingType?: Type, headMessage?: DiagnosticMessage): Signature { + const typeArguments: TypeNode[] = getEffectiveTypeArguments(node); + + let partialSignatures: Signature[]; + if (node.kind === SyntaxKind.BindExpression || + node.kind === SyntaxKind.BindToExpression || + isPartialApplication(node)) { + partialSignatures = []; } const candidates = candidatesOutArray || []; @@ -12975,7 +13053,7 @@ namespace ts { reorderCandidates(signatures, candidates); if (!candidates.length) { reportError(Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target); - return resolveErrorCall(node, partialSignaturesOutArray); + return resolveErrorCall(node); } const args = getEffectiveCallArguments(node); @@ -13001,7 +13079,7 @@ namespace ts { // We do not need to call `getEffectiveArgumentCount` here as it only // applies when calculating the number of arguments for a decorator. for (let i = node.kind === SyntaxKind.TaggedTemplateExpression ? 1 : 0; i < args.length; i++) { - if (isContextSensitive(args[i])) { + if (isContextSensitive(getEffectiveArgument(node, args, i))) { if (!excludeArgument) { excludeArgument = new Array(args.length); } @@ -13051,7 +13129,7 @@ namespace ts { // Whether the call is an error is determined by assignability of the arguments. The subtype pass // is just important for choosing the best signature. So in the case where there is only one // signature, the subtype pass is useless. So skipping it is an optimization. - if (candidates.length > 1 && !partialSignaturesOutArray) { + if (candidates.length > 1 && !partialSignatures) { result = chooseOverload(candidates, subtypeRelation); } if (!result) { @@ -13061,8 +13139,24 @@ namespace ts { resultOfFailedInference = undefined; result = chooseOverload(candidates, assignableRelation); } - if (result || some(partialSignaturesOutArray)) { - return result; + if (some(partialSignatures)) { + const returnType = node.kind === SyntaxKind.BindExpression || node.kind === SyntaxKind.BindToExpression + ? createTypeFromSignatures(map(partialSignatures, getSignatureWithoutThis)) + : getPartialApplicationOfSignatures(args, partialSignatures); + result = createSignature( + /*isConstruct*/ false, + /*typeParameters*/ undefined, + /*typeParameter*/ undefined, + /*parameters*/ emptyArray, + returnType, + /*typePredicate*/ undefined, + /*minArgumentCount*/ 0, + /*hasRestParameter*/ false, + /*hasLiteralTypes*/ false + ); + } + if (result) { + return propagateNullReturnType(result, propagatingType || neverType); } // No signatures were applicable. Now report errors based on the last applicable signature with @@ -13117,7 +13211,7 @@ namespace ts { } } - return resolveErrorCall(node, partialSignaturesOutArray); + return resolveErrorCall(node); function reportError(message: DiagnosticMessage, arg0?: string, arg1?: string, arg2?: string): void { let errorInfo: DiagnosticMessageChain; @@ -13164,11 +13258,11 @@ namespace ts { } const index = excludeArgument ? indexOf(excludeArgument, true) : -1; if (index < 0) { - if (partialSignaturesOutArray) { + if (partialSignatures) { const partialCandidate = originalCandidate.typeParameters ? getPartialSignatureInstantiation(originalCandidate, typeArgumentTypes) : originalCandidate; - partialSignaturesOutArray.push(partialCandidate); + partialSignatures.push(partialCandidate); continue nextCandidate; } return candidate; @@ -13202,7 +13296,7 @@ namespace ts { } } - function resolveCallExpression(node: CallExpression, partialSignaturesOutArray: Signature[], candidatesOutArray: Signature[]): Signature { + function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature { if (node.expression.kind === SyntaxKind.SuperKeyword) { const superType = checkSuperExpression(node.expression); if (superType !== unknownType) { @@ -13211,21 +13305,23 @@ namespace ts { const baseTypeNode = getClassExtendsHeritageClauseElement(getContainingClass(node)); if (baseTypeNode) { const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments); - return resolveCall(node, baseConstructors, partialSignaturesOutArray, candidatesOutArray); + return resolveCall(node, baseConstructors, candidatesOutArray); } } - return resolveUntypedCall(node, partialSignaturesOutArray); + return resolveUntypedCall(node); } - const funcType = checkNonNullExpression(node.expression); - if (funcType === silentNeverType) { + const propagateNull = node.flags & NodeFlags.PropagateNull; + const funcType = propagateNull ? checkExpression(node.expression) : checkNonNullExpression(node.expression); + const type = propagateNull ? getNonNullableType(funcType) : funcType; + if (type === silentNeverType) { return silentNeverSignature; } - const apparentType = getApparentType(funcType); + const apparentType = getApparentType(type); if (apparentType === unknownType) { // Another error has already been reported - return resolveErrorCall(node, partialSignaturesOutArray); + return resolveErrorCall(node); } // Technically, this signatures list may be incomplete. We are taking the apparent type, @@ -13238,27 +13334,29 @@ namespace ts { // TS 1.0 Spec: 4.12 // In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual // types are provided for the argument expressions, and the result is always of type Any. - if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { + if (isUntypedFunctionCall(type, apparentType, callSignatures.length, constructSignatures.length)) { // The unknownType indicates that an error already occurred (and was reported). No // need to report another error in this case. - if (funcType !== unknownType && node.typeArguments) { + if (type !== unknownType && node.typeArguments) { error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); } - return resolveUntypedCall(node, partialSignaturesOutArray); + return resolveUntypedCall(node); } // If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call. // TypeScript employs overload resolution in typed function calls in order to support functions // with multiple call signatures. if (!callSignatures.length) { if (constructSignatures.length) { - error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType)); + error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(type)); } else { error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType)); } - return resolveErrorCall(node, partialSignaturesOutArray); + return resolveErrorCall(node); } - return resolveCall(node, callSignatures, partialSignaturesOutArray, candidatesOutArray); + + const propagatingType = propagateNull ? getTypeWithFacts(funcType, TypeFacts.EQUndefinedOrNull) : neverType; + return resolveCall(node, callSignatures, candidatesOutArray, propagatingType); } /** @@ -13292,8 +13390,10 @@ namespace ts { } } - let expressionType = checkNonNullExpression(node.expression); - if (expressionType === silentNeverType) { + const propagateNull = node.flags & NodeFlags.PropagateNull; + const funcType = propagateNull ? checkExpression(node.expression) : checkNonNullExpression(node.expression); + const type = propagateNull ? getNonNullableType(funcType) : funcType; + if (type === silentNeverType) { return silentNeverSignature; } @@ -13302,51 +13402,53 @@ namespace ts { // function call, but using the construct signatures as the initial set of candidate // signatures for overload resolution. The result type of the function call becomes // the result type of the operation. - expressionType = getApparentType(expressionType); - if (expressionType === unknownType) { + const apparentType = getApparentType(type); + if (apparentType === unknownType) { // Another error has already been reported - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } // If the expression is a class of abstract type, then it cannot be instantiated. // Note, only class declarations can be declared abstract. // In the case of a merged class-module or class-interface declaration, // only the class declaration node will have the Abstract flag set. - const valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol); + const valueDecl = apparentType.symbol && getClassLikeDeclarationOfSymbol(apparentType.symbol); if (valueDecl && getModifierFlags(valueDecl) & ModifierFlags.Abstract) { error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(valueDecl.name)); - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } // TS 1.0 spec: 4.11 // If expressionType is of type Any, Args can be any argument // list and the result of the operation is of type Any. - if (isTypeAny(expressionType)) { + if (isTypeAny(apparentType)) { if (node.typeArguments) { error(node, Diagnostics.Untyped_function_calls_may_not_accept_type_arguments); } - return resolveUntypedCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveUntypedCall(node); } // Technically, this signatures list may be incomplete. We are taking the apparent type, // but we are not including construct signatures that may have been added to the Object or // Function interface, since they have none by default. This is a bit of a leap of faith // that the user will not add any. - const constructSignatures = getSignaturesOfType(expressionType, SignatureKind.Construct); + const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); if (constructSignatures.length) { if (!isConstructorAccessible(node, constructSignatures[0])) { - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } - return resolveCall(node, constructSignatures, /*partialSignaturesOutArray*/ undefined, candidatesOutArray); + + const propagatingType = propagateNull ? getTypeWithFacts(funcType, TypeFacts.EQUndefinedOrNull) : neverType; + return resolveCall(node, constructSignatures, candidatesOutArray, propagatingType); } // If expressionType's apparent type is an object type with no construct signatures but // one or more call signatures, the expression is processed as a function call. A compile-time // error occurs if the result of the function call is not Void. The type of the result of the // operation is Any. It is an error to have a Void this type. - const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call); + const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); if (callSignatures.length) { - const signature = resolveCall(node, callSignatures, /*partialSignaturesOutArray*/ undefined, candidatesOutArray); + const signature = resolveCall(node, callSignatures, candidatesOutArray); if (getReturnTypeOfSignature(signature) !== voidType) { error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); } @@ -13357,7 +13459,7 @@ namespace ts { } error(node, Diagnostics.Cannot_use_new_with_an_expression_whose_type_lacks_a_call_or_construct_signature); - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } function isConstructorAccessible(node: NewExpression, signature: Signature) { @@ -13408,22 +13510,22 @@ namespace ts { if (apparentType === unknownType) { // Another error has already been reported - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); if (isUntypedFunctionCall(tagType, apparentType, callSignatures.length, constructSignatures.length)) { - return resolveUntypedCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveUntypedCall(node); } if (!callSignatures.length) { error(node, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType)); - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } - return resolveCall(node, callSignatures, /*partialSignaturesOutArray*/ undefined, candidatesOutArray); + return resolveCall(node, callSignatures, candidatesOutArray); } /** @@ -13455,13 +13557,13 @@ namespace ts { const funcType = checkExpression(node.expression); const apparentType = getApparentType(funcType); if (apparentType === unknownType) { - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { - return resolveUntypedCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveUntypedCall(node); } const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node); @@ -13470,62 +13572,69 @@ namespace ts { errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType)); errorInfo = chainDiagnosticMessages(errorInfo, headMessage); diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo)); - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } - return resolveCall(node, callSignatures, /*partialSignaturesOutArray*/ undefined, candidatesOutArray, headMessage); + return resolveCall(node, callSignatures, candidatesOutArray, /*propagatingType*/ undefined, headMessage); } function resolvePipelineExpression(node: PipelineExpression, candidatesOutArray: Signature[]): Signature { - const funcType = checkExpression(node.right); - const apparentType = getApparentType(funcType); + const type = isPartialApplication(node.right) + ? checkNonNullExpression(node.right.expression) + : checkNonNullExpression(node.right); + + const apparentType = getApparentType(type); if (apparentType === unknownType) { - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); - if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { - return resolveUntypedCall(node, /*partialSignaturesOutArray*/ undefined); + if (isUntypedFunctionCall(type, apparentType, callSignatures.length, constructSignatures.length)) { + return resolveUntypedCall(node); } if (!callSignatures.length) { - return resolveErrorCall(node, /*partialSignaturesOutArray*/ undefined); + return resolveErrorCall(node); } - return resolveCall(node, callSignatures, /*partialSignaturesOutArray*/ undefined, candidatesOutArray); + return resolveCall(node, callSignatures, candidatesOutArray); } - function resolveBindExpression(node: BindExpression | BindToExpression, partialSignaturesOutArray: Signature[], candidatesOutArray?: Signature[]): Signature { - const funcType = checkExpression(node.expression); - const apparentType = getApparentType(funcType); + function resolveBindExpression(node: BindExpression | BindToExpression, candidatesOutArray?: Signature[]): Signature { + const type = checkNonNullExpression(node.expression); + if (type === silentNeverType) { + return silentNeverSignature; + } + + const apparentType = getApparentType(type); if (apparentType === unknownType) { - return resolveErrorCall(node, partialSignaturesOutArray); + return resolveErrorCall(node); } const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct); - if (isUntypedFunctionCall(funcType, apparentType, callSignatures.length, constructSignatures.length)) { - return resolveUntypedCall(node, partialSignaturesOutArray); + if (isUntypedFunctionCall(type, apparentType, callSignatures.length, constructSignatures.length)) { + return resolveUntypedCall(node); } if (!callSignatures.length) { if (constructSignatures.length) { - error(node.expression, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType)); + error(node.expression, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(type)); } else { error(node.expression, Diagnostics.Cannot_bind_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, typeToString(apparentType)); } - return resolveErrorCall(node, partialSignaturesOutArray); + return resolveErrorCall(node); } - return resolveCall(node, callSignatures, partialSignaturesOutArray, candidatesOutArray); + return resolveCall(node, callSignatures, candidatesOutArray); } - function resolveSignature(node: CallLikeExpression, partialSignaturesOutArray: Signature[], candidatesOutArray?: Signature[]): Signature { + function resolveSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature { switch (node.kind) { case SyntaxKind.CallExpression: - return resolveCallExpression(node, partialSignaturesOutArray, candidatesOutArray); + return resolveCallExpression(node, candidatesOutArray); case SyntaxKind.NewExpression: return resolveNewExpression(node, candidatesOutArray); case SyntaxKind.TaggedTemplateExpression: @@ -13536,7 +13645,7 @@ namespace ts { return resolvePipelineExpression(node, candidatesOutArray); case SyntaxKind.BindExpression: case SyntaxKind.BindToExpression: - return resolveBindExpression(node, partialSignaturesOutArray, candidatesOutArray); + return resolveBindExpression(node, candidatesOutArray); } Debug.fail("Branch in 'resolveSignature' should be unreachable."); } @@ -13554,7 +13663,7 @@ namespace ts { return cached; } links.resolvedSignature = resolvingSignature; - const result = resolveSignature(node, /*partialSignaturesOutArray*/ undefined, candidatesOutArray); + const result = resolveSignature(node, candidatesOutArray); // If signature resolution originated in control flow type analysis (for example to compute the // assigned type in a flow assignment) we don't cache the result as it may be based on temporary // types from the control flow analysis. @@ -13562,26 +13671,6 @@ namespace ts { return result; } - function getResolvedPartialSignatures(node: CallLikeExpression): Signature[] { - const links = getNodeLinks(node); - // If getResolvedSignature has already been called, we will have cached the resolvedSignature. - // However, it is possible that either candidatesOutArray was not passed in the first time, - // or that a different candidatesOutArray was passed in. Therefore, we need to redo the work - // to correctly fill the candidatesOutArray. - const cached = links.resolvedPartialSignatures; - if (cached && cached !== resolvingPartialSignatures) { - return cached; - } - links.resolvedPartialSignatures = resolvingPartialSignatures; - const partialSignatures: Signature[] = []; - resolveSignature(node, partialSignatures); - // If signature resolution originated in control flow type analysis (for example to compute the - // assigned type in a flow assignment) we don't cache the result as it may be based on temporary - // types from the control flow analysis. - links.resolvedPartialSignatures = flowLoopStart === flowLoopCount ? partialSignatures : cached; - return partialSignatures; - } - function getResolvedOrAnySignature(node: CallLikeExpression) { // If we're already in the process of resolving the given signature, don't resolve again as // that could cause infinite recursion. Instead, return anySignature. @@ -13605,9 +13694,7 @@ namespace ts { // Grammar checking; stop grammar-checking if checkGrammarTypeArguments return true checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments); - const partialApplication = node.kind === SyntaxKind.CallExpression && forEach(node.arguments, isPositionalOrPositionalSpreadElement); - const signatures = partialApplication && getResolvedPartialSignatures(node); - const signature = !partialApplication && getResolvedSignature(node); + const signature = getResolvedSignature(node); if (node.expression.kind === SyntaxKind.SuperKeyword) { return voidType; @@ -13645,9 +13732,7 @@ namespace ts { return resolveExternalModuleTypeByLiteral(node.arguments[0]); } - return partialApplication ? - getPartialApplicationOfSignatures(node.arguments, signatures) : - getReturnTypeOfSignature(signature); + return getReturnTypeOfSignature(signature); } function getPartialApplicationOfSignatures(argumentList: Expression[], signatures: Signature[]) { @@ -13655,13 +13740,7 @@ namespace ts { for (const signature of signatures) { partialSignatures.push(getPartialApplicationOfSignature(argumentList, signature)); } - - const partialType = createObjectType(ObjectFlags.Anonymous); - (partialType).members = emptySymbols; - (partialType).properties = emptyArray; - (partialType).callSignatures = partialSignatures; - (partialType).constructSignatures = emptyArray; - return partialType; + return createTypeFromSignatures(partialSignatures); } function getPartialApplicationOfSignature(argumentList: Expression[], signature: Signature) { @@ -13670,12 +13749,11 @@ namespace ts { uniqueParameterNames[parameter.name] = true; } - let positionalParameters: TransientSymbol[]; + const positionalParameters: TransientSymbol[] = []; let positionalRestParameter: TransientSymbol; let position = 0; for (let i = 0; i < argumentList.length; i++) { const argument = argumentList[i]; - // const inRestParameter = signature.hasRestParameter && i >= signature.parameters.length; const parameter = i < signature.parameters.length ? signature.parameters[i] : lastOrUndefined(signature.parameters); if (isPositionalElement(argument)) { let ordinalPosition: number; @@ -13686,9 +13764,7 @@ namespace ts { ordinalPosition = position; position++; } - if (!positionalParameters) { - positionalParameters = []; - } + const type = getTypeAtPosition(signature, i); const previousParameter = positionalParameters[ordinalPosition]; if (previousParameter) { @@ -13697,17 +13773,14 @@ namespace ts { } previousParameter.type = getIntersectionType([previousParameter.type, type]); } - else if (parameter) { - const sym = createTransientSymbol(parameter, type); - if (sym) - positionalParameters[ordinalPosition] = createTransientSymbol(parameter, type); - } else { - // TODO(rbuckton): implicit any error? - const name = getUniqueName(uniqueParameterNames, `arg${ordinalPosition}`); - const newParameter = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, name); - newParameter.type = type; - positionalParameters[ordinalPosition] = newParameter; + const name = i < signature.parameters.length - (signature.hasRestParameter ? 1 : 0) ? + parameter.name : + getUniqueName(uniqueParameterNames, parameter ? parameter.name : `arg${ordinalPosition}`); + positionalParameters[ordinalPosition] = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, name); + positionalParameters[ordinalPosition].target = parameter; + positionalParameters[ordinalPosition].type = type; + positionalParameters[ordinalPosition].transientSymbolIsRest = false; } } else if (isPositionalSpreadElement(argument)) { @@ -13718,28 +13791,26 @@ namespace ts { } positionalRestParameter.type = getIntersectionType([positionalRestParameter.type, type]); } - else if (parameter) { - positionalRestParameter = createTransientSymbol(parameter, type); - positionalRestParameter.transientSymbolIsRest = true; - } else { - // TODO(rbuckton): implicit any error? - const name = getUniqueName(uniqueParameterNames, `args`); + const name = parameter ? parameter.name : getUniqueName(uniqueParameterNames, `args`); positionalRestParameter = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, name); + positionalRestParameter.target = parameter; positionalRestParameter.type = type; positionalRestParameter.transientSymbolIsRest = true; } } } - positionalParameters = append(positionalParameters, positionalRestParameter) || []; + if (positionalRestParameter) { + positionalParameters.push(positionalRestParameter); + } for (let i = 0; i < positionalParameters.length; i++) { if (!positionalParameters[i]) { const name = getUniqueName(uniqueParameterNames, `arg${i}`); + // TODO(rbuckton): implicit any error positionalParameters[i] = createSymbol(SymbolFlags.Transient | SymbolFlags.Variable, name); positionalParameters[i].type = anyType; - // TODO(rbuckton): implicit any error } } @@ -13759,7 +13830,7 @@ namespace ts { let uniqueName = name; let index = 0; while (uniqueNames[uniqueName]) { - uniqueName = `${name}_${index}`; + uniqueName = `${name}${index}`; index++; } uniqueNames[uniqueName] = true; @@ -15000,6 +15071,10 @@ namespace ts { return getTypeFacts(leftType) & TypeFacts.Truthy ? includeFalsyTypes(rightType, getFalsyFlags(strictNullChecks ? leftType : getBaseTypeOfLiteralType(rightType))) : leftType; + case SyntaxKind.QuestionQuestionToken: + return getTypeFacts(leftType) & TypeFacts.EQUndefinedOrNull ? + getBestChoiceType(getNonNullableType(leftType), rightType) : + leftType; case SyntaxKind.BarBarToken: return getTypeFacts(leftType) & TypeFacts.Falsy ? getBestChoiceType(removeDefinitelyFalsyTypes(leftType), rightType) : diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 1c63716ab0a..1ed9e0adcff 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1037,12 +1037,13 @@ namespace ts { } function emitPropertyAccessExpression(node: PropertyAccessExpression) { + const propagatesNull = node.flags & NodeFlags.PropagateNull; let indentBeforeDot = false; let indentAfterDot = false; if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { const dotRangeStart = node.expression.end; - const dotRangeEnd = skipTrivia(currentText, node.expression.end) + 1; - const dotToken = { kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; + const dotRangeEnd = skipTrivia(currentText, node.expression.end) + (propagatesNull ? 2 : 1); + const dotToken = { kind: propagatesNull ? SyntaxKind.QuestionDotToken : SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; indentBeforeDot = needsIndentation(node, node.expression, dotToken); indentAfterDot = needsIndentation(node, dotToken, node.name); } @@ -1050,8 +1051,8 @@ namespace ts { emitExpression(node.expression); increaseIndentIf(indentBeforeDot); - const shouldEmitDotDot = !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); - write(shouldEmitDotDot ? ".." : "."); + const shouldEmitDotDot = !propagatesNull && !indentBeforeDot && needsDotDotForPropertyAccess(node.expression); + write(shouldEmitDotDot ? ".." : propagatesNull ? "?." : "."); increaseIndentIf(indentAfterDot); emit(node.name); @@ -1078,25 +1079,28 @@ namespace ts { function emitElementAccessExpression(node: ElementAccessExpression) { emitExpression(node.expression); - write("["); + write(node.flags & NodeFlags.PropagateNull ? "?.[" : "["); emitExpression(node.argumentExpression); write("]"); } function emitBindToExpression(node: BindToExpression) { emitExpression(node.targetExpression); - write("::"); + write(node.flags & NodeFlags.PropagateNull ? "?::" : "::"); emitExpression(node.expression); } function emitBindExpression(node: BindExpression) { - write("::"); + write(node.flags & NodeFlags.PropagateNull ? "?::" : "::"); emitExpression(node.expression); } function emitCallExpression(node: CallExpression) { emitExpression(node.expression); emitTypeArguments(node, node.typeArguments); + if (node.flags & NodeFlags.PropagateNull) { + write("?."); + } emitExpressionList(node, node.arguments, ListFormat.CallExpressionArguments); } @@ -1104,6 +1108,9 @@ namespace ts { write("new "); emitExpression(node.expression); emitTypeArguments(node, node.typeArguments); + if (node.flags & NodeFlags.PropagateNull) { + write("?."); + } emitExpressionList(node, node.arguments, ListFormat.NewExpressionArguments); } @@ -1119,7 +1126,6 @@ namespace ts { emit(node.type); write(">"); } - emitExpression(node.expression); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 273a1f6f823..7c20fa60dd2 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -437,8 +437,8 @@ namespace ts { return node; } - export function createElementAccess(expression: Expression, index: number | Expression, location?: TextRange) { - const node = createNode(SyntaxKind.ElementAccessExpression, location); + export function createElementAccess(expression: Expression, index: number | Expression, location?: TextRange, flags?: NodeFlags) { + const node = createNode(SyntaxKind.ElementAccessExpression, location, flags); node.expression = parenthesizeForAccess(expression); node.argumentExpression = typeof index === "number" ? createLiteral(index) : index; return node; @@ -446,7 +446,7 @@ namespace ts { export function updateElementAccess(node: ElementAccessExpression, expression: Expression, argumentExpression: Expression) { if (node.expression !== expression || node.argumentExpression !== argumentExpression) { - return updateNode(createElementAccess(expression, argumentExpression, node), node); + return updateNode(createElementAccess(expression, argumentExpression, /*location*/ node, node.flags), node); } return node; } @@ -653,16 +653,16 @@ namespace ts { if (whenFalse) { // second overload node.questionToken = questionTokenOrWhenTrue; - node.whenTrue = whenTrueOrWhenFalse; + node.whenTrue = parenthesizeForConditionalResult(whenTrueOrWhenFalse); node.colonToken = colonTokenOrLocation; - node.whenFalse = whenFalse; + node.whenFalse = parenthesizeForConditionalResult(whenFalse); } else { // first overload node.questionToken = createToken(SyntaxKind.QuestionToken); - node.whenTrue = questionTokenOrWhenTrue; + node.whenTrue = parenthesizeForConditionalResult(questionTokenOrWhenTrue); node.colonToken = createToken(SyntaxKind.ColonToken); - node.whenFalse = whenTrueOrWhenFalse; + node.whenFalse = parenthesizeForConditionalResult(whenTrueOrWhenFalse); } return node; } @@ -1561,10 +1561,18 @@ namespace ts { return createBinary(left, SyntaxKind.EqualsEqualsEqualsToken, right); } + export function createEquality(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.EqualsEqualsToken, right); + } + export function createStrictInequality(left: Expression, right: Expression) { return createBinary(left, SyntaxKind.ExclamationEqualsEqualsToken, right); } + export function createInequality(left: Expression, right: Expression) { + return createBinary(left, SyntaxKind.ExclamationEqualsToken, right); + } + export function createAdd(left: Expression, right: Expression) { return createBinary(left, SyntaxKind.PlusToken, right); } @@ -2392,6 +2400,16 @@ namespace ts { return condition; } + export function parenthesizeForConditionalResult(expression: Expression) { + const conditionalPrecedence = getOperatorPrecedence(SyntaxKind.ConditionalExpression, SyntaxKind.ColonToken); + const emittedExpression = skipPartiallyEmittedExpressions(expression); + const expressionPrecedence = getExpressionPrecedence(emittedExpression); + if (compareValues(expressionPrecedence, conditionalPrecedence) === Comparison.LessThan) { + return createParen(expression); + } + return expression; + } + /** * Wraps an expression in parentheses if it is needed in order to use the expression * as the expression of a NewExpression node. diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a5b1cd3f8ae..9502f761c8a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -209,6 +209,8 @@ namespace ts { visitNode(cbNode, (node).whenTrue) || visitNode(cbNode, (node).colonToken) || visitNode(cbNode, (node).whenFalse); + case SyntaxKind.PositionalElement: + return visitNode(cbNode, (node).literal); case SyntaxKind.SpreadElement: return visitNode(cbNode, (node).expression); case SyntaxKind.Block: @@ -3373,6 +3375,7 @@ namespace ts { switch (token()) { case SyntaxKind.BarGreaterThanToken: return 1; + case SyntaxKind.QuestionQuestionToken: case SyntaxKind.BarBarToken: return 2; case SyntaxKind.AmpersandAmpersandToken: @@ -3745,7 +3748,11 @@ namespace ts { function parseSuperExpression(): MemberExpression { const expression = parseTokenNode(); - if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.DotToken || token() === SyntaxKind.OpenBracketToken) { + if (token() === SyntaxKind.OpenParenToken || + token() === SyntaxKind.DotToken || + token() === SyntaxKind.OpenBracketToken || + token() === SyntaxKind.QuestionDotToken || + token() === SyntaxKind.QuestionDotOpenBracketToken) { return expression; } @@ -3996,9 +4003,12 @@ namespace ts { function parseMemberExpressionRest(expression: LeftHandSideExpression): MemberExpression { while (true) { - const dotToken = parseOptionalToken(SyntaxKind.DotToken); - if (dotToken) { + if (token() === SyntaxKind.DotToken || token() === SyntaxKind.QuestionDotToken) { const propertyAccess = createNode(SyntaxKind.PropertyAccessExpression, expression.pos); + if (token() === SyntaxKind.QuestionDotToken) { + propertyAccess.flags |= NodeFlags.PropagateNull; + } + nextToken(); propertyAccess.expression = expression; propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames*/ true); expression = finishNode(propertyAccess); @@ -4014,8 +4024,12 @@ 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)) { + if (!inDecoratorContext() && (token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.QuestionDotOpenBracketToken)) { const indexedAccess = createNode(SyntaxKind.ElementAccessExpression, expression.pos); + if (token() === SyntaxKind.QuestionDotOpenBracketToken) { + indexedAccess.flags |= NodeFlags.PropagateNull; + } + nextToken(); indexedAccess.expression = expression; // It's not uncommon for a user to write: "new Type[]". @@ -4043,8 +4057,9 @@ namespace ts { continue; } - if (parseOptional(SyntaxKind.ColonColonToken)) { + if (token() === SyntaxKind.ColonColonToken) { const bindExpression = createNode(SyntaxKind.BindToExpression, expression.pos); + nextToken(); bindExpression.targetExpression = expression; bindExpression.expression = parseMemberExpressionOrHigher(); expression = finishNode(bindExpression); @@ -4058,37 +4073,35 @@ namespace ts { function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression { while (true) { expression = parseMemberExpressionRest(expression); + let typeArguments: NodeArray; if (token() === SyntaxKind.LessThanToken) { // See if this is the start of a generic invocation. If so, consume it and // keep checking for postfix expressions. Otherwise, it's just a '<' that's // part of an arithmetic expression. Break out so we consume it higher in the // stack. - const typeArguments = tryParse(parseTypeArgumentsInExpression); + typeArguments = tryParse(parseTypeArgumentsInExpression); if (!typeArguments) { return expression; } - - 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.arguments = parseArgumentList(); - expression = finishNode(callExpr); - continue; + else if (token() !== SyntaxKind.OpenParenToken && token() !== SyntaxKind.QuestionDotOpenParenToken) { + return expression; } - - return expression; + const callExpr = createNode(SyntaxKind.CallExpression, expression.pos); + if (token() === SyntaxKind.QuestionDotOpenParenToken) { + callExpr.flags |= NodeFlags.PropagateNull; + } + callExpr.expression = expression; + callExpr.typeArguments = typeArguments; + callExpr.arguments = parseArgumentList(); + expression = finishNode(callExpr); } } function parseArgumentList() { - parseExpected(SyntaxKind.OpenParenToken); + if (!parseOptional(SyntaxKind.QuestionDotOpenParenToken)) { + parseExpected(SyntaxKind.OpenParenToken); + } const result = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); parseExpected(SyntaxKind.CloseParenToken); return result; @@ -4115,6 +4128,7 @@ namespace ts { function canFollowTypeArgumentsInExpression(): boolean { switch (token()) { case SyntaxKind.OpenParenToken: // foo( + case SyntaxKind.QuestionDotOpenParenToken: // foo?.( // this case are the only case where this token can legally follow a type argument // list. So we definitely want to treat this as a type arg list. @@ -4437,7 +4451,10 @@ namespace ts { parseExpected(SyntaxKind.NewKeyword); node.expression = parseMemberExpressionOrHigher(); node.typeArguments = tryParse(parseTypeArgumentsInExpression); - if (node.typeArguments || token() === SyntaxKind.OpenParenToken) { + if (node.typeArguments || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.QuestionDotOpenParenToken) { + if (token() === SyntaxKind.QuestionDotOpenParenToken) { + node.flags |= NodeFlags.PropagateNull; + } node.arguments = parseArgumentList(); } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index ab2a53d6321..129a283cd7d 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -170,6 +170,10 @@ namespace ts { "&&": SyntaxKind.AmpersandAmpersandToken, "||": SyntaxKind.BarBarToken, "?": SyntaxKind.QuestionToken, + "??": SyntaxKind.QuestionQuestionToken, + "?.": SyntaxKind.QuestionDotToken, + "?.[": SyntaxKind.QuestionDotOpenBracketToken, + "?.(": SyntaxKind.QuestionDotOpenParenToken, ":": SyntaxKind.ColonToken, "::": SyntaxKind.ColonColonToken, "=": SyntaxKind.EqualsToken, @@ -1550,6 +1554,20 @@ namespace ts { pos++; return token = SyntaxKind.GreaterThanToken; case CharacterCodes.question: + if (text.charCodeAt(pos + 1) === CharacterCodes.question) { + return pos += 2, token = SyntaxKind.QuestionQuestionToken; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.dot) { + if (text.charCodeAt(pos + 2) === CharacterCodes.openParen) { + return pos += 3, token = SyntaxKind.QuestionDotOpenParenToken; + } + if (text.charCodeAt(pos + 2) === CharacterCodes.openBracket) { + return pos += 3, token = SyntaxKind.QuestionDotOpenBracketToken; + } + if (!isDigit(text.charCodeAt(pos + 2))) { + return pos += 2, token = SyntaxKind.QuestionDotToken; + } + } pos++; return token = SyntaxKind.QuestionToken; case CharacterCodes.openBracket: diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 5b0c08a78df..3e278c49cf5 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -69,12 +69,18 @@ namespace ts { return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue); case SyntaxKind.CallExpression: return visitCallExpression(node as CallExpression); + case SyntaxKind.NewExpression: + return visitNewExpression(node as NewExpression); case SyntaxKind.OperatorExpression: return visitOperatorExpression(node as OperatorExpression); case SyntaxKind.BindExpression: return visitBindExpression(node as BindExpression); case SyntaxKind.BindToExpression: return visitBindToExpression(node as BindToExpression); + case SyntaxKind.PropertyAccessExpression: + return visitPropertyAccess(node as PropertyAccessExpression); + case SyntaxKind.ElementAccessExpression: + return visitElementAccess(node as ElementAccessExpression); default: return visitEachChild(node, visitor, context); } @@ -159,28 +165,25 @@ namespace ts { visitNode(node.right, noDestructuringValue ? visitorNoDestructuringValue : visitor, isExpression) ); case SyntaxKind.BarGreaterThanToken: - return transformPipelineExpression(node); + return transformPipelineExpression(node); + case SyntaxKind.QuestionQuestionToken: + return transformCoalesceExpression(node); } return visitEachChild(node, visitor, context); } - function transformPipelineExpression(node: BinaryExpression) { + function transformPipelineExpression(node: PipelineExpression) { const argumentList: Expression[] = []; - let expression = node.left; - if (expression.kind === SyntaxKind.ParenthesizedExpression) { - // comma expressions are right-deep - expression = (expression).expression; - while (expression.kind === SyntaxKind.BinaryExpression && - (expression).operatorToken.kind === SyntaxKind.CommaToken) { - argumentList.push(visitNode((expression).left, visitor, isExpression)); - expression = (expression).right; - } + if (node.left.kind === SyntaxKind.ParenthesizedExpression) { + collectPipelineArguments((node.left).expression, argumentList); + } + else { + argumentList.push(visitNode(node.left, visitor, isExpression)); } - argumentList.push(visitNode(expression, visitor, isExpression)); const func = visitNode(node.right, visitor, isExpression); if (func.kind === SyntaxKind.ArrowFunction || func.kind === SyntaxKind.FunctionExpression) { - return createCall(func, /*typeArguments*/ undefined, argumentList); + return createCall(func, /*typeArguments*/ undefined, argumentList, node); } else { const parameterList: ParameterDeclaration[] = []; @@ -204,7 +207,38 @@ namespace ts { ) ), /*typeArguments*/ undefined, - argumentList + argumentList, + node + ); + } + } + + function collectPipelineArguments(node: Expression, argumentList: Expression[]) { + if (isCommaExpression(node)) { + collectPipelineArguments(node.left, argumentList); + collectPipelineArguments(node.right, argumentList); + } + else { + argumentList.push(visitNode(node, visitor, isExpression)); + } + } + + function transformCoalesceExpression(node: CoalesceExpression): Expression { + const left = visitNode(node.left, visitor, isExpression); + const right = visitNode(node.right, visitor, isExpression); + if (isIdentifier(left)) { + return createConditional( + createInequality(left, createNull()), + left, + right + ); + } + else { + const temp = createTempVariable(hoistVariableDeclaration); + return createConditional( + createInequality(createAssignment(temp, left), createNull()), + temp, + right ); } } @@ -442,65 +476,181 @@ namespace ts { return body; } + function propagateNull(finishExpression: (node: T, nullableExpression: Expression) => Expression, node: T, nullableExpression: Expression): Expression; + function propagateNull(finishExpression: (node: T, nullableExpression: Expression, data: U) => Expression, node: T, nullableExpression: Expression, data: U): Expression; + function propagateNull(finishExpression: (node: T, nullableExpression: Expression, data: U) => Expression, node: T, nullableExpression: Expression, data?: U): Expression { + if (node.flags & NodeFlags.PropagateNull) { + if (isIdentifier(nullableExpression)) { + return createConditional( + createEquality(nullableExpression, createNull()), + nullableExpression, + finishExpression(node, nullableExpression, data), + node + ); + } + else { + const temp = createTempVariable(hoistVariableDeclaration); + return createConditional( + createEquality( + createAssignment(temp, nullableExpression), + createNull() + ), + temp, + finishExpression(node, temp, data), + node + ); + } + } + return finishExpression(node, nullableExpression, data); + } + function visitCallExpression(node: CallExpression): Expression { - let expression = visitNode(node.expression, visitor, isExpression) as Expression; - if (!forEach(node.arguments, isPositionalOrPositionalSpreadElement)) { - return updateCall( - node, - expression, - /*typeArguments*/ undefined, - visitNodes(node.arguments, visitor, isExpression)); + return propagateNull(finishCallExpression, node, visitNode(node.expression, visitor, isExpression)); + } + + function finishCallExpression(node: CallExpression, expression: Expression) { + if (forEach(node.arguments, isPositionalOrPositionalSpreadElement)) { + return transformPartialCallExpression(node, expression); } - else { - const expressionTemp = createTempVariable(hoistVariableDeclaration); - const argumentList: Expression[] = []; - const pendingExpressions: Expression[] = [createAssignment(expressionTemp, expression)]; - const positionalParameters: ParameterDeclaration[] = []; - let positionalRestParameter: ParameterDeclaration; - let position = 0; - for (let i = 0; i < node.arguments.length; i++) { - let argument = node.arguments[i]; - if (isPositionalElement(argument)) { - if (argument.literal) { - position = +argument.literal.text; - } - const parameter = positionalParameters[position] || (positionalParameters[position] = createParameter()); - argument = parameter.name; - position++; - } - else if (isPositionalSpreadElement(argument)) { - const parameter = positionalRestParameter || (positionalRestParameter = createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, createToken(SyntaxKind.DotDotDotToken))); - argument = createSpreadElement(parameter.name); - } - else { - argument = visitNode(argument, visitor, isExpression); - const temp = createTempVariable(hoistVariableDeclaration); - pendingExpressions.push(createAssignment(temp, isSpreadElement(argument) ? argument.expression : argument)); - argument = isSpreadElement(argument) ? createSpreadElement(temp) : temp; - } - argumentList.push(argument); - } - if (positionalRestParameter) { - positionalParameters.push(positionalRestParameter); - } - for (let i = 0; i < positionalParameters.length; i++) { - if (!positionalParameters[i]) { - positionalParameters[i] = createParameter(); - } - } - pendingExpressions.push( - createArrowFunction( - /*modifiers*/ undefined, - /*typeParameters*/ undefined, - positionalParameters, - /*type*/ undefined, - /*equalsGreaterThanToken*/ createToken(SyntaxKind.EqualsGreaterThanToken), - updateCall(node, expressionTemp, /*typeArguments*/ undefined, argumentList), - /*location*/ node - ) + if (node.flags & NodeFlags.PropagateNull) { + return setOriginalNode( + createCall( + expression, + /*typeArguments*/ undefined, + visitNodes(node.arguments, visitor, isExpression), + node + ), + node ); - return inlineExpressions(pendingExpressions); } + return updateCall( + node, + expression, + /*typeArguments*/ undefined, + visitNodes(node.arguments, visitor, isExpression), + ) + } + + function transformPartialCallExpression(node: CallExpression, expression: Expression) { + const expressionTemp = createTempVariable(hoistVariableDeclaration); + const argumentList: Expression[] = []; + const pendingExpressions: Expression[] = [createAssignment(expressionTemp, expression)]; + const positionalParameters: ParameterDeclaration[] = []; + let positionalRestParameter: ParameterDeclaration; + let position = 0; + for (let i = 0; i < node.arguments.length; i++) { + let argument = node.arguments[i]; + if (isPositionalElement(argument)) { + if (argument.literal) { + position = +argument.literal.text; + } + const parameter = positionalParameters[position] || (positionalParameters[position] = createParameter()); + argument = parameter.name; + position++; + } + else if (isPositionalSpreadElement(argument)) { + const parameter = positionalRestParameter || (positionalRestParameter = createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, createToken(SyntaxKind.DotDotDotToken))); + argument = createSpreadElement(parameter.name); + } + else { + argument = visitNode(argument, visitor, isExpression); + const temp = createTempVariable(hoistVariableDeclaration); + pendingExpressions.push(createAssignment(temp, isSpreadElement(argument) ? argument.expression : argument)); + argument = isSpreadElement(argument) ? createSpreadElement(temp) : temp; + } + argumentList.push(argument); + } + if (positionalRestParameter) { + positionalParameters.push(positionalRestParameter); + } + for (let i = 0; i < positionalParameters.length; i++) { + if (!positionalParameters[i]) { + positionalParameters[i] = createParameter(); + } + } + pendingExpressions.push( + createArrowFunction( + /*modifiers*/ undefined, + /*typeParameters*/ undefined, + positionalParameters, + /*type*/ undefined, + /*equalsGreaterThanToken*/ createToken(SyntaxKind.EqualsGreaterThanToken), + setOriginalNode( + createCall(expressionTemp, /*typeArguments*/ undefined, argumentList, node), + node + ), + /*location*/ node + ) + ); + return inlineExpressions(pendingExpressions); + } + + function visitNewExpression(node: NewExpression): Expression { + return propagateNull(finishNewExpression, node, visitNode(node.expression, visitor, isExpression)); + } + + function finishNewExpression(node: NewExpression, expression: Expression) { + if (node.flags & NodeFlags.PropagateNull) { + return setOriginalNode( + createNew( + expression, + /*typeArguments*/ undefined, + visitNodes(node.arguments, visitor, isExpression), + /*location*/ node + ), + node + ); + } + return updateNew( + node, + expression, + /*typeArguments*/ undefined, + visitNodes(node.arguments, visitor, isExpression), + ); + } + + function visitPropertyAccess(node: PropertyAccessExpression): Expression { + return propagateNull(finishPropertyAccess, node, visitNode(node.expression, visitor, isExpression)); + } + + function finishPropertyAccess(node: PropertyAccessExpression, expression: Expression) { + if (node.flags & NodeFlags.PropagateNull) { + return setOriginalNode( + createPropertyAccess( + expression, + node.name, + node + ), + node + ); + } + return updatePropertyAccess( + node, + expression, + node.name + ); + } + + function visitElementAccess(node: ElementAccessExpression): Expression { + return propagateNull(finishElementAccess, node, visitNode(node.expression, visitor, isExpression)); + } + + function finishElementAccess(node: ElementAccessExpression, expression: Expression) { + if (node.flags & NodeFlags.PropagateNull) { + return setOriginalNode( + createElementAccess( + expression, + visitNode(node.argumentExpression, visitor, isExpression), + node + ), + node + ); + } + return updateElementAccess( + node, + expression, + visitNode(node.argumentExpression, visitor, isExpression), + ); } function visitOperatorExpression(node: OperatorExpression) { @@ -551,37 +701,27 @@ namespace ts { if (operand.kind === SyntaxKind.PropertyAccessExpression || operand.kind === SyntaxKind.ElementAccessExpression) { const { target, thisArg } = createCallBinding(operand, hoistVariableDeclaration); - return createFunctionBind( - target, - thisArg, - [], - node - ) + return createFunctionBind(target, thisArg, [], node); } else { - return createFunctionBind( - operand, - createNull(), - [], - node - ); + return createFunctionBind(operand, createNull(), [], node); } } function visitBindToExpression(node: BindToExpression) { + const targetExpression = visitNode(node.targetExpression, visitor, isExpression); + const expression = visitNode(node.expression, visitor, isLeftHandSideExpression); + if (isIdentifier(targetExpression)) { + return createFunctionBind(expression, targetExpression, [], node); + } const thisArg = createTempVariable(context.hoistVariableDeclaration); return createComma( createAssignment( thisArg, - visitNode(node.targetExpression, visitor, isExpression), + targetExpression, node.targetExpression ), - createFunctionBind( - visitNode(node.expression, visitor, isLeftHandSideExpression), - thisArg, - [], - node - ) + createFunctionBind(expression, thisArg, [], node) ); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5b3fc674e70..28672517778 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -53,7 +53,7 @@ namespace ts { TemplateMiddle, TemplateTail, // Punctuation - OpenBraceToken, + OpenBraceToken, // FirstPunctuation CloseBraceToken, OpenParenToken, CloseParenToken, @@ -62,10 +62,21 @@ namespace ts { DotToken, DotDotDotToken, SemicolonToken, - BarGreaterThanToken, CommaToken, EqualsGreaterThanToken, - LessThanToken, + QuestionDotToken, + QuestionDotOpenBracketToken, + QuestionDotOpenParenToken, + QuestionToken, + ColonToken, + AtToken, + PlusPlusToken, // FirstPrefixUnaryOperator, FirstPostfixUnaryOperator + MinusMinusToken, // LastPostfixUnaryOperator + ExclamationToken, + TildeToken, // LastPrefixUnaryOperator + TildePlusToken, + TildeMinusToken, + LessThanToken, // FirstBinaryOperator LessThanSlashToken, GreaterThanToken, LessThanEqualsToken, @@ -80,27 +91,20 @@ namespace ts { AsteriskAsteriskToken, SlashToken, PercentToken, - PlusPlusToken, - MinusMinusToken, LessThanLessThanToken, GreaterThanGreaterThanToken, GreaterThanGreaterThanGreaterThanToken, AmpersandToken, BarToken, CaretToken, - ExclamationToken, - TildeToken, - TildePlusToken, - TildeMinusToken, AmpersandAmpersandToken, BarBarToken, - QuestionToken, - ColonToken, + QuestionQuestionToken, ColonColonToken, - AtToken, + BarGreaterThanToken, // Assignments - EqualsToken, - PlusEqualsToken, + EqualsToken, // FirstAssignment + PlusEqualsToken, // FirstCompoundAssignment MinusEqualsToken, AsteriskEqualsToken, AsteriskAsteriskEqualsToken, @@ -111,7 +115,7 @@ namespace ts { GreaterThanGreaterThanGreaterThanEqualsToken, AmpersandEqualsToken, BarEqualsToken, - CaretEqualsToken, + CaretEqualsToken, // LastAssignment, LastCompoundAssignment, LastBinaryOperator, LastPunctuation // Identifiers Identifier, // Reserved words @@ -382,10 +386,6 @@ namespace ts { // Enum value count Count, // Markers - FirstAssignment = EqualsToken, - LastAssignment = CaretEqualsToken, - FirstCompoundAssignment = PlusEqualsToken, - LastCompoundAssignment = CaretEqualsToken, FirstReservedWord = BreakKeyword, LastReservedWord = WithKeyword, FirstKeyword = BreakKeyword, @@ -394,6 +394,16 @@ namespace ts { LastFutureReservedWord = YieldKeyword, FirstTypeNode = TypePredicate, LastTypeNode = LiteralType, + FirstAssignment = EqualsToken, + LastAssignment = CaretEqualsToken, + FirstCompoundAssignment = PlusEqualsToken, + LastCompoundAssignment = CaretEqualsToken, + FirstPrefixUnaryOperator = PlusPlusToken, + LastPrefixUnaryOperator = TildeToken, + FirstPostfixUnaryOperator = PlusPlusToken, + LastPostfixUnaryOperator = MinusMinusToken, + FirstBinaryOperator = LessThanToken, + LastBinaryOperator = CaretEqualsToken, FirstPunctuation = OpenBraceToken, LastPunctuation = CaretEqualsToken, FirstToken = Unknown, @@ -404,8 +414,6 @@ namespace ts { LastLiteralToken = NoSubstitutionTemplateLiteral, FirstTemplateToken = NoSubstitutionTemplateLiteral, LastTemplateToken = TemplateTail, - FirstBinaryOperator = LessThanToken, - LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, LastJSDocNode = JSDocLiteralType, @@ -420,25 +428,26 @@ 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 - HasClassExtends = 1 << 10, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding) - HasDecorators = 1 << 11, // If the file has decorators (initialized by binding) - HasParamDecorators = 1 << 12, // If the file has parameter decorators (initialized by binding) - HasAsyncFunctions = 1 << 13, // If the file has async functions (initialized by binding) - HasSpreadAttribute = 1 << 14, // If the file as JSX spread attributes (initialized by binding) - HasRestAttribute = 1 << 15, // If the file has object destructure elements - DisallowInContext = 1 << 16, // If node was parsed in a context where 'in-expressions' are not allowed - YieldContext = 1 << 17, // If node was parsed in the 'yield' context created when parsing a generator - DecoratorContext = 1 << 18, // If node was parsed as part of a decorator - AwaitContext = 1 << 19, // If node was parsed in the 'await' context created when parsing an async function - ThisNodeHasError = 1 << 20, // If the parser encountered an error when parsing the code that created this node - JavaScriptFile = 1 << 21, // If node was parsed in a JavaScript - ThisNodeOrAnySubNodesHasError = 1 << 22, // If this node or any of its children had an error - HasAggregatedChildData = 1 << 23, // If we've computed data from children and cached it in this node + PropagateNull = 1 << 5, // Expression + 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 + HasClassExtends = 1 << 11, // If the file has a non-ambient class with an extends clause in ES5 or lower (initialized by binding) + HasDecorators = 1 << 12, // If the file has decorators (initialized by binding) + HasParamDecorators = 1 << 13, // If the file has parameter decorators (initialized by binding) + HasAsyncFunctions = 1 << 14, // If the file has async functions (initialized by binding) + HasSpreadAttribute = 1 << 15, // If the file as JSX spread attributes (initialized by binding) + HasRestAttribute = 1 << 16, // If the file has object destructure elements + DisallowInContext = 1 << 17, // If node was parsed in a context where 'in-expressions' are not allowed + YieldContext = 1 << 18, // If node was parsed in the 'yield' context created when parsing a generator + DecoratorContext = 1 << 19, // If node was parsed as part of a decorator + AwaitContext = 1 << 20, // If node was parsed in the 'await' context created when parsing an async function + ThisNodeHasError = 1 << 21, // If the parser encountered an error when parsing the code that created this node + JavaScriptFile = 1 << 22, // If node was parsed in a JavaScript + ThisNodeOrAnySubNodesHasError = 1 << 23, // If this node or any of its children had an error + HasAggregatedChildData = 1 << 24, // If we've computed data from children and cached it in this node BlockScoped = Let | Const, @@ -1140,6 +1149,7 @@ namespace ts { export type LogicalOperator = SyntaxKind.AmpersandAmpersandToken | SyntaxKind.BarBarToken + | SyntaxKind.QuestionQuestionToken ; // see: https://tc39.github.io/ecma262/#prod-LogicalANDExpression @@ -1181,6 +1191,7 @@ namespace ts { export type BinaryOperator = AssignmentOperatorOrHigher | SyntaxKind.BarGreaterThanToken + | SyntaxKind.QuestionQuestionToken | SyntaxKind.CommaToken ; @@ -1194,12 +1205,22 @@ namespace ts { right: Expression; } + export type CommaToken = Token; export type BarGreaterThanToken = Token; + export type QuestionQuestionToken = Token; + + export interface CommaExpression extends BinaryExpression { + operatorToken: CommaToken; + } export interface PipelineExpression extends BinaryExpression { operatorToken: BarGreaterThanToken; } + export interface CoalesceExpression extends BinaryExpression { + operatorToken: QuestionQuestionToken; + } + export type AssignmentOperatorToken = Token; export interface AssignmentExpression extends BinaryExpression { @@ -2777,7 +2798,6 @@ namespace ts { flags?: NodeCheckFlags; // Set of flags specific to Node resolvedType?: Type; // Cached type of type node resolvedSignature?: Signature; // Cached signature of signature node or call expression - resolvedPartialSignatures?: Signature[]; resolvedSymbol?: Symbol; // Cached name resolution result resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result maybeTypePredicate?: boolean; // Cached check whether call expression might reference a type predicate diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 88e66420bef..1e88ed02905 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2135,36 +2135,40 @@ namespace ts { case SyntaxKind.ParenthesizedExpression: case SyntaxKind.OmittedExpression: case SyntaxKind.RawExpression: - return 19; + case SyntaxKind.BindExpression: + return 20; case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.PropertyAccessExpression: case SyntaxKind.ElementAccessExpression: - return 18; + case SyntaxKind.BindToExpression: + return 19; case SyntaxKind.NewExpression: - return hasArguments ? 18 : 17; + return hasArguments ? 19 : 18; case SyntaxKind.CallExpression: - return 17; + return 18; case SyntaxKind.PostfixUnaryExpression: - return 16; + return 17; case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.TypeOfExpression: case SyntaxKind.VoidExpression: case SyntaxKind.DeleteExpression: case SyntaxKind.AwaitExpression: - return 15; + return 16; case SyntaxKind.BinaryExpression: switch (operatorKind) { case SyntaxKind.ExclamationToken: case SyntaxKind.TildeToken: - return 15; + return 16; case SyntaxKind.AsteriskAsteriskToken: + return 15; + case SyntaxKind.AsteriskToken: case SyntaxKind.SlashToken: case SyntaxKind.PercentToken: @@ -2206,6 +2210,7 @@ namespace ts { return 6; case SyntaxKind.BarBarToken: + case SyntaxKind.QuestionQuestionToken: return 5; case SyntaxKind.EqualsToken: @@ -2231,11 +2236,12 @@ namespace ts { } case SyntaxKind.ConditionalExpression: - return 4; + return operatorKind === SyntaxKind.QuestionToken ? 4 : 3; case SyntaxKind.YieldExpression: return 2; + case SyntaxKind.PositionalElement: case SyntaxKind.SpreadElement: return 1; @@ -3903,6 +3909,7 @@ namespace ts { case SyntaxKind.AmpersandAmpersandToken: case SyntaxKind.BarBarToken: case SyntaxKind.BarGreaterThanToken: + case SyntaxKind.QuestionQuestionToken: return true; } return false; @@ -3912,6 +3919,11 @@ namespace ts { return node.kind === SyntaxKind.BinaryExpression; } + export function isCommaExpression(node: Node): node is CommaExpression { + return isBinaryExpression(node) + && node.operatorToken.kind === SyntaxKind.CommaToken + } + export function isConditionalExpression(node: Node): node is ConditionalExpression { return node.kind === SyntaxKind.ConditionalExpression; } @@ -3943,6 +3955,11 @@ namespace ts { return isPositionalElement(node) || isPositionalSpreadElement(node); } + export function isPartialApplication(node: Node): node is CallExpression { + return node.kind === SyntaxKind.CallExpression + && forEach((node).arguments, isPositionalOrPositionalSpreadElement); + } + export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { return node.kind === SyntaxKind.ExpressionWithTypeArguments; } diff --git a/tests/baselines/reference/parser.bindTo.esnext.1.symbols b/tests/baselines/reference/parser.bindTo.esnext.1.symbols new file mode 100644 index 00000000000..761cd8693dd --- /dev/null +++ b/tests/baselines/reference/parser.bindTo.esnext.1.symbols @@ -0,0 +1,72 @@ +=== tests/cases/conformance/parser/esnext/parser.bindTo.esnext.1.ts === +// single argument +declare function f1(this: number): string; +>f1 : Symbol(f1, Decl(parser.bindTo.esnext.1.ts, 0, 0)) +>this : Symbol(this, Decl(parser.bindTo.esnext.1.ts, 1, 20)) + +1 :: f1; +1 :: f1(); +// multiple steps +declare function f2(this: string): boolean; +>f2 : Symbol(f2, Decl(parser.bindTo.esnext.1.ts, 3, 10)) +>this : Symbol(this, Decl(parser.bindTo.esnext.1.ts, 5, 20)) + +1 :: f1() :: f2(); +// use cases +declare function map(this: Iterable, cb: (value: T) => U): Iterable; +>map : Symbol(map, Decl(parser.bindTo.esnext.1.ts, 6, 18)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 8, 21)) +>U : Symbol(U, Decl(parser.bindTo.esnext.1.ts, 8, 23)) +>this : Symbol(this, Decl(parser.bindTo.esnext.1.ts, 8, 27)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 8, 21)) +>cb : Symbol(cb, Decl(parser.bindTo.esnext.1.ts, 8, 45)) +>value : Symbol(value, Decl(parser.bindTo.esnext.1.ts, 8, 51)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 8, 21)) +>U : Symbol(U, Decl(parser.bindTo.esnext.1.ts, 8, 23)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>U : Symbol(U, Decl(parser.bindTo.esnext.1.ts, 8, 23)) + +declare function filter(this: Iterable, cb: (value: T) => boolean): Iterable; +>filter : Symbol(filter, Decl(parser.bindTo.esnext.1.ts, 8, 80)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 9, 24)) +>this : Symbol(this, Decl(parser.bindTo.esnext.1.ts, 9, 27)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 9, 24)) +>cb : Symbol(cb, Decl(parser.bindTo.esnext.1.ts, 9, 45)) +>value : Symbol(value, Decl(parser.bindTo.esnext.1.ts, 9, 51)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 9, 24)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 9, 24)) + +declare function reduce(this: Iterable, cb: (memo: T, value: T) => T, initial: T): T; +>reduce : Symbol(reduce, Decl(parser.bindTo.esnext.1.ts, 9, 86)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 10, 24)) +>this : Symbol(this, Decl(parser.bindTo.esnext.1.ts, 10, 27)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 10, 24)) +>cb : Symbol(cb, Decl(parser.bindTo.esnext.1.ts, 10, 45)) +>memo : Symbol(memo, Decl(parser.bindTo.esnext.1.ts, 10, 51)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 10, 24)) +>value : Symbol(value, Decl(parser.bindTo.esnext.1.ts, 10, 59)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 10, 24)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 10, 24)) +>initial : Symbol(initial, Decl(parser.bindTo.esnext.1.ts, 10, 75)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 10, 24)) +>T : Symbol(T, Decl(parser.bindTo.esnext.1.ts, 10, 24)) + +[1, 2, 3] + :: map(x => x * 2) +>x : Symbol(x, Decl(parser.bindTo.esnext.1.ts, 12, 11)) +>x : Symbol(x, Decl(parser.bindTo.esnext.1.ts, 12, 11)) + + :: filter(x => x > 2) +>x : Symbol(x, Decl(parser.bindTo.esnext.1.ts, 13, 14)) +>x : Symbol(x, Decl(parser.bindTo.esnext.1.ts, 13, 14)) + + :: reduce((x, y) => x + y, 0); +>x : Symbol(x, Decl(parser.bindTo.esnext.1.ts, 14, 15)) +>y : Symbol(y, Decl(parser.bindTo.esnext.1.ts, 14, 17)) +>x : Symbol(x, Decl(parser.bindTo.esnext.1.ts, 14, 15)) +>y : Symbol(y, Decl(parser.bindTo.esnext.1.ts, 14, 17)) + diff --git a/tests/baselines/reference/parser.bindTo.esnext.1.types b/tests/baselines/reference/parser.bindTo.esnext.1.types new file mode 100644 index 00000000000..7901ec37e69 --- /dev/null +++ b/tests/baselines/reference/parser.bindTo.esnext.1.types @@ -0,0 +1,102 @@ +=== tests/cases/conformance/parser/esnext/parser.bindTo.esnext.1.ts === +// single argument +declare function f1(this: number): string; +>f1 : (this: number) => string +>this : number + +1 :: f1; +>f1 : any + +1 :: f1(); +>1 :: f1() : string +>f1 : any + +// multiple steps +declare function f2(this: string): boolean; +>f2 : (this: string) => boolean +>this : string + +1 :: f1() :: f2(); +>1 :: f1() :: f2() : boolean +>1 :: f1() : string +>f1 : any +>f2 : any + +// use cases +declare function map(this: Iterable, cb: (value: T) => U): Iterable; +>map : (this: Iterable, cb: (value: T) => U) => Iterable +>T : T +>U : U +>this : Iterable +>Iterable : Iterable +>T : T +>cb : (value: T) => U +>value : T +>T : T +>U : U +>Iterable : Iterable +>U : U + +declare function filter(this: Iterable, cb: (value: T) => boolean): Iterable; +>filter : (this: Iterable, cb: (value: T) => boolean) => Iterable +>T : T +>this : Iterable +>Iterable : Iterable +>T : T +>cb : (value: T) => boolean +>value : T +>T : T +>Iterable : Iterable +>T : T + +declare function reduce(this: Iterable, cb: (memo: T, value: T) => T, initial: T): T; +>reduce : (this: Iterable, cb: (memo: T, value: T) => T, initial: T) => T +>T : T +>this : Iterable +>Iterable : Iterable +>T : T +>cb : (memo: T, value: T) => T +>memo : T +>T : T +>value : T +>T : T +>T : T +>initial : T +>T : T +>T : T + +[1, 2, 3] +>[1, 2, 3] :: map(x => x * 2) :: filter(x => x > 2) :: reduce((x, y) => x + y, 0) : number +>[1, 2, 3] :: map(x => x * 2) :: filter(x => x > 2) : Iterable +>[1, 2, 3] :: map(x => x * 2) : Iterable +>[1, 2, 3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 + + :: map(x => x * 2) +>map : any +>x => x * 2 : (x: number) => number +>x : number +>x * 2 : number +>x : number +>2 : 2 + + :: filter(x => x > 2) +>filter : any +>x => x > 2 : (x: number) => boolean +>x : number +>x > 2 : boolean +>x : number +>2 : 2 + + :: reduce((x, y) => x + y, 0); +>reduce : any +>(x, y) => x + y : (x: number, y: number) => number +>x : number +>y : number +>x + y : number +>x : number +>y : number +>0 : 0 + diff --git a/tests/baselines/reference/parser.partialApplication.esnext.1.symbols b/tests/baselines/reference/parser.partialApplication.esnext.1.symbols new file mode 100644 index 00000000000..05e5ce7a4dd --- /dev/null +++ b/tests/baselines/reference/parser.partialApplication.esnext.1.symbols @@ -0,0 +1,114 @@ +=== tests/cases/conformance/parser/esnext/parser.partialApplication.esnext.1.ts === +declare function f(a: number, b: number, ...c: number[]): void; +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) +>a : Symbol(a, Decl(parser.partialApplication.esnext.1.ts, 0, 19)) +>b : Symbol(b, Decl(parser.partialApplication.esnext.1.ts, 0, 29)) +>c : Symbol(c, Decl(parser.partialApplication.esnext.1.ts, 0, 40)) + +declare const o: { m(a: number, b: number, ...c: number[]): void; }; +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>a : Symbol(a, Decl(parser.partialApplication.esnext.1.ts, 1, 21)) +>b : Symbol(b, Decl(parser.partialApplication.esnext.1.ts, 1, 31)) +>c : Symbol(c, Decl(parser.partialApplication.esnext.1.ts, 1, 42)) + +// positional elements +f(?, 0); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +f(0, ?); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +f(?, ?); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +o.m(?, 0); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +o.m(0, ?); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +o.m(?, ?); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +// ordinal positional elements +f(?0, 0); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +f(0, ?0); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +f(?0, ?1); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +f(?1, ?0); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +o.m(?0, 0); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +o.m(0, ?0); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +o.m(?0, ?1); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +o.m(?1, ?0); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +// positional spread element +f(0, 1, ...); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +f(0, 1, ..., 2); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +o.m(0, 1, ...); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +o.m(0, 1, ..., 2); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +// mixed +f(?, 0, ...); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +f(0, ?, ...); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +f(0, 1, ..., ?0); +>f : Symbol(f, Decl(parser.partialApplication.esnext.1.ts, 0, 0)) + +o.m(?, 0, ...); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +o.m(0, ?, ...); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + +o.m(0, 1, ..., ?0); +>o.m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) +>o : Symbol(o, Decl(parser.partialApplication.esnext.1.ts, 1, 13)) +>m : Symbol(m, Decl(parser.partialApplication.esnext.1.ts, 1, 18)) + diff --git a/tests/baselines/reference/parser.partialApplication.esnext.1.types b/tests/baselines/reference/parser.partialApplication.esnext.1.types new file mode 100644 index 00000000000..8cbefc7a71e --- /dev/null +++ b/tests/baselines/reference/parser.partialApplication.esnext.1.types @@ -0,0 +1,184 @@ +=== tests/cases/conformance/parser/esnext/parser.partialApplication.esnext.1.ts === +declare function f(a: number, b: number, ...c: number[]): void; +>f : (a: number, b: number, ...c: number[]) => void +>a : number +>b : number +>c : number[] + +declare const o: { m(a: number, b: number, ...c: number[]): void; }; +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>a : number +>b : number +>c : number[] + +// positional elements +f(?, 0); +>f(?, 0) : (a: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 + +f(0, ?); +>f(0, ?) : (b: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 + +f(?, ?); +>f(?, ?) : (a: number, b: number) => void +>f : (a: number, b: number, ...c: number[]) => void + +o.m(?, 0); +>o.m(?, 0) : (a: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 + +o.m(0, ?); +>o.m(0, ?) : (b: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 + +o.m(?, ?); +>o.m(?, ?) : (a: number, b: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void + +// ordinal positional elements +f(?0, 0); +>f(?0, 0) : (a: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 + +f(0, ?0); +>f(0, ?0) : (b: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 + +f(?0, ?1); +>f(?0, ?1) : (a: number, b: number) => void +>f : (a: number, b: number, ...c: number[]) => void + +f(?1, ?0); +>f(?1, ?0) : (b: number, a: number) => void +>f : (a: number, b: number, ...c: number[]) => void + +o.m(?0, 0); +>o.m(?0, 0) : (a: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 + +o.m(0, ?0); +>o.m(0, ?0) : (b: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 + +o.m(?0, ?1); +>o.m(?0, ?1) : (a: number, b: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void + +o.m(?1, ?0); +>o.m(?1, ?0) : (b: number, a: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void + +// positional spread element +f(0, 1, ...); +>f(0, 1, ...) : (...c: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>1 : 1 +>... : any +> : undefined + +f(0, 1, ..., 2); +>f(0, 1, ..., 2) : (...c: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>1 : 1 +>... : any +> : undefined +>2 : 2 + +o.m(0, 1, ...); +>o.m(0, 1, ...) : (...c: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>1 : 1 +>... : any +> : undefined + +o.m(0, 1, ..., 2); +>o.m(0, 1, ..., 2) : (...c: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>1 : 1 +>... : any +> : undefined +>2 : 2 + +// mixed +f(?, 0, ...); +>f(?, 0, ...) : (a: number, ...c: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>... : any +> : undefined + +f(0, ?, ...); +>f(0, ?, ...) : (b: number, ...c: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>... : any +> : undefined + +f(0, 1, ..., ?0); +>f(0, 1, ..., ?0) : (c0: number, ...c: number) => void +>f : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>1 : 1 +>... : any +> : undefined + +o.m(?, 0, ...); +>o.m(?, 0, ...) : (a: number, ...c: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>... : any +> : undefined + +o.m(0, ?, ...); +>o.m(0, ?, ...) : (b: number, ...c: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>... : any +> : undefined + +o.m(0, 1, ..., ?0); +>o.m(0, 1, ..., ?0) : (c0: number, ...c: number) => void +>o.m : (a: number, b: number, ...c: number[]) => void +>o : { m(a: number, b: number, ...c: number[]): void; } +>m : (a: number, b: number, ...c: number[]) => void +>0 : 0 +>1 : 1 +>... : any +> : undefined + diff --git a/tests/baselines/reference/parser.pipeline.esnext.1.symbols b/tests/baselines/reference/parser.pipeline.esnext.1.symbols new file mode 100644 index 00000000000..19028fdc7ef --- /dev/null +++ b/tests/baselines/reference/parser.pipeline.esnext.1.symbols @@ -0,0 +1,121 @@ +=== tests/cases/conformance/parser/esnext/parser.pipeline.esnext.1.ts === +// single argument +declare function f1(x: number): string; +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 1, 20)) + +1 |> f1; +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) + +1 |> f1(?); +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) + +(1) |> f1; +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) + +(1) |> f1(?); +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) + +// multiple argument +declare function f2(x: number, y: number): string; +>f2 : Symbol(f2, Decl(parser.pipeline.esnext.1.ts, 5, 13)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 7, 20)) +>y : Symbol(y, Decl(parser.pipeline.esnext.1.ts, 7, 30)) + +(1, 2) |> f2; +>f2 : Symbol(f2, Decl(parser.pipeline.esnext.1.ts, 5, 13)) + +(1, 2) |> f2(?, ?); +>f2 : Symbol(f2, Decl(parser.pipeline.esnext.1.ts, 5, 13)) + +// multiple steps +declare function f3(x: string): boolean; +>f3 : Symbol(f3, Decl(parser.pipeline.esnext.1.ts, 9, 19)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 11, 20)) + +declare function f4(x: string, y: number): boolean; +>f4 : Symbol(f4, Decl(parser.pipeline.esnext.1.ts, 11, 40)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 12, 20)) +>y : Symbol(y, Decl(parser.pipeline.esnext.1.ts, 12, 30)) + +1 |> f1 |> f3; +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) +>f3 : Symbol(f3, Decl(parser.pipeline.esnext.1.ts, 9, 19)) + +1 |> f1(?) |> f3; +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) +>f3 : Symbol(f3, Decl(parser.pipeline.esnext.1.ts, 9, 19)) + +1 |> f1 |> f3(?); +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) +>f3 : Symbol(f3, Decl(parser.pipeline.esnext.1.ts, 9, 19)) + +1 |> f1(?) |> f3(?); +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) +>f3 : Symbol(f3, Decl(parser.pipeline.esnext.1.ts, 9, 19)) + +(1 |> f1(?), 2) |> f4; +>f1 : Symbol(f1, Decl(parser.pipeline.esnext.1.ts, 0, 0)) +>f4 : Symbol(f4, Decl(parser.pipeline.esnext.1.ts, 11, 40)) + +// use case +declare function map(source: Iterable, cb: (value: T) => U): Iterable; +>map : Symbol(map, Decl(parser.pipeline.esnext.1.ts, 17, 22)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 19, 21)) +>U : Symbol(U, Decl(parser.pipeline.esnext.1.ts, 19, 23)) +>source : Symbol(source, Decl(parser.pipeline.esnext.1.ts, 19, 27)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 19, 21)) +>cb : Symbol(cb, Decl(parser.pipeline.esnext.1.ts, 19, 47)) +>value : Symbol(value, Decl(parser.pipeline.esnext.1.ts, 19, 53)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 19, 21)) +>U : Symbol(U, Decl(parser.pipeline.esnext.1.ts, 19, 23)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>U : Symbol(U, Decl(parser.pipeline.esnext.1.ts, 19, 23)) + +declare function filter(source: Iterable, cb: (value: T) => boolean): Iterable; +>filter : Symbol(filter, Decl(parser.pipeline.esnext.1.ts, 19, 82)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 20, 24)) +>source : Symbol(source, Decl(parser.pipeline.esnext.1.ts, 20, 27)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 20, 24)) +>cb : Symbol(cb, Decl(parser.pipeline.esnext.1.ts, 20, 47)) +>value : Symbol(value, Decl(parser.pipeline.esnext.1.ts, 20, 53)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 20, 24)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 20, 24)) + +declare function reduce(source: Iterable, cb: (memo: T, value: T) => T, initial: T): T; +>reduce : Symbol(reduce, Decl(parser.pipeline.esnext.1.ts, 20, 88)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 21, 24)) +>source : Symbol(source, Decl(parser.pipeline.esnext.1.ts, 21, 27)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 21, 24)) +>cb : Symbol(cb, Decl(parser.pipeline.esnext.1.ts, 21, 47)) +>memo : Symbol(memo, Decl(parser.pipeline.esnext.1.ts, 21, 53)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 21, 24)) +>value : Symbol(value, Decl(parser.pipeline.esnext.1.ts, 21, 61)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 21, 24)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 21, 24)) +>initial : Symbol(initial, Decl(parser.pipeline.esnext.1.ts, 21, 77)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 21, 24)) +>T : Symbol(T, Decl(parser.pipeline.esnext.1.ts, 21, 24)) + +[1, 2, 3] + |> map(?, x => x * 2) +>map : Symbol(map, Decl(parser.pipeline.esnext.1.ts, 17, 22)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 23, 13)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 23, 13)) + + |> filter(?, x => x > 2) +>filter : Symbol(filter, Decl(parser.pipeline.esnext.1.ts, 19, 82)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 24, 16)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 24, 16)) + + |> reduce(?, (x, y) => x + y, 0); +>reduce : Symbol(reduce, Decl(parser.pipeline.esnext.1.ts, 20, 88)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 25, 18)) +>y : Symbol(y, Decl(parser.pipeline.esnext.1.ts, 25, 20)) +>x : Symbol(x, Decl(parser.pipeline.esnext.1.ts, 25, 18)) +>y : Symbol(y, Decl(parser.pipeline.esnext.1.ts, 25, 20)) + diff --git a/tests/baselines/reference/parser.pipeline.esnext.1.types b/tests/baselines/reference/parser.pipeline.esnext.1.types new file mode 100644 index 00000000000..ceba719ea84 --- /dev/null +++ b/tests/baselines/reference/parser.pipeline.esnext.1.types @@ -0,0 +1,187 @@ +=== tests/cases/conformance/parser/esnext/parser.pipeline.esnext.1.ts === +// single argument +declare function f1(x: number): string; +>f1 : (x: number) => string +>x : number + +1 |> f1; +>1 |> f1 : string +>1 : 1 +>f1 : (x: number) => string + +1 |> f1(?); +>1 |> f1(?) : string +>1 : 1 +>f1(?) : (x: number) => string +>f1 : (x: number) => string + +(1) |> f1; +>(1) |> f1 : string +>(1) : 1 +>1 : 1 +>f1 : (x: number) => string + +(1) |> f1(?); +>(1) |> f1(?) : string +>(1) : 1 +>1 : 1 +>f1(?) : (x: number) => string +>f1 : (x: number) => string + +// multiple argument +declare function f2(x: number, y: number): string; +>f2 : (x: number, y: number) => string +>x : number +>y : number + +(1, 2) |> f2; +>(1, 2) |> f2 : string +>(1, 2) : 2 +>1, 2 : 2 +>1 : 1 +>2 : 2 +>f2 : (x: number, y: number) => string + +(1, 2) |> f2(?, ?); +>(1, 2) |> f2(?, ?) : string +>(1, 2) : 2 +>1, 2 : 2 +>1 : 1 +>2 : 2 +>f2(?, ?) : (x: number, y: number) => string +>f2 : (x: number, y: number) => string + +// multiple steps +declare function f3(x: string): boolean; +>f3 : (x: string) => boolean +>x : string + +declare function f4(x: string, y: number): boolean; +>f4 : (x: string, y: number) => boolean +>x : string +>y : number + +1 |> f1 |> f3; +>1 |> f1 |> f3 : boolean +>1 |> f1 : string +>1 : 1 +>f1 : (x: number) => string +>f3 : (x: string) => boolean + +1 |> f1(?) |> f3; +>1 |> f1(?) |> f3 : boolean +>1 |> f1(?) : string +>1 : 1 +>f1(?) : (x: number) => string +>f1 : (x: number) => string +>f3 : (x: string) => boolean + +1 |> f1 |> f3(?); +>1 |> f1 |> f3(?) : boolean +>1 |> f1 : string +>1 : 1 +>f1 : (x: number) => string +>f3(?) : (x: string) => boolean +>f3 : (x: string) => boolean + +1 |> f1(?) |> f3(?); +>1 |> f1(?) |> f3(?) : boolean +>1 |> f1(?) : string +>1 : 1 +>f1(?) : (x: number) => string +>f1 : (x: number) => string +>f3(?) : (x: string) => boolean +>f3 : (x: string) => boolean + +(1 |> f1(?), 2) |> f4; +>(1 |> f1(?), 2) |> f4 : boolean +>(1 |> f1(?), 2) : 2 +>1 |> f1(?), 2 : 2 +>1 |> f1(?) : string +>1 : 1 +>f1(?) : (x: number) => string +>f1 : (x: number) => string +>2 : 2 +>f4 : (x: string, y: number) => boolean + +// use case +declare function map(source: Iterable, cb: (value: T) => U): Iterable; +>map : (source: Iterable, cb: (value: T) => U) => Iterable +>T : T +>U : U +>source : Iterable +>Iterable : Iterable +>T : T +>cb : (value: T) => U +>value : T +>T : T +>U : U +>Iterable : Iterable +>U : U + +declare function filter(source: Iterable, cb: (value: T) => boolean): Iterable; +>filter : (source: Iterable, cb: (value: T) => boolean) => Iterable +>T : T +>source : Iterable +>Iterable : Iterable +>T : T +>cb : (value: T) => boolean +>value : T +>T : T +>Iterable : Iterable +>T : T + +declare function reduce(source: Iterable, cb: (memo: T, value: T) => T, initial: T): T; +>reduce : (source: Iterable, cb: (memo: T, value: T) => T, initial: T) => T +>T : T +>source : Iterable +>Iterable : Iterable +>T : T +>cb : (memo: T, value: T) => T +>memo : T +>T : T +>value : T +>T : T +>T : T +>initial : T +>T : T +>T : T + +[1, 2, 3] +>[1, 2, 3] |> map(?, x => x * 2) |> filter(?, x => x > 2) |> reduce(?, (x, y) => x + y, 0) : number +>[1, 2, 3] |> map(?, x => x * 2) |> filter(?, x => x > 2) : Iterable +>[1, 2, 3] |> map(?, x => x * 2) : Iterable +>[1, 2, 3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 + + |> map(?, x => x * 2) +>map(?, x => x * 2) : (source: Iterable) => Iterable +>map : (source: Iterable, cb: (value: T) => U) => Iterable +>x => x * 2 : (x: number) => number +>x : number +>x * 2 : number +>x : number +>2 : 2 + + |> filter(?, x => x > 2) +>filter(?, x => x > 2) : (source: Iterable) => Iterable +>filter : (source: Iterable, cb: (value: T) => boolean) => Iterable +>x => x > 2 : (x: number) => boolean +>x : number +>x > 2 : boolean +>x : number +>2 : 2 + + |> reduce(?, (x, y) => x + y, 0); +>reduce(?, (x, y) => x + y, 0) : (source: Iterable) => number +>reduce : (source: Iterable, cb: (memo: T, value: T) => T, initial: T) => T +>(x, y) => x + y : (x: number, y: number) => number +>x : number +>y : number +>x + y : number +>x : number +>y : number +>0 : 0 + diff --git a/tests/cases/conformance/parser/esnext/parser.bindTo.esnext.1.ts b/tests/cases/conformance/parser/esnext/parser.bindTo.esnext.1.ts new file mode 100644 index 00000000000..b2191fe7374 --- /dev/null +++ b/tests/cases/conformance/parser/esnext/parser.bindTo.esnext.1.ts @@ -0,0 +1,18 @@ +// @target: esnext +// @noEmit: true +// @lib: es2015 +// single argument +declare function f1(this: number): string; +1 :: f1; +1 :: f1(); +// multiple steps +declare function f2(this: string): boolean; +1 :: f1() :: f2(); +// use cases +declare function map(this: Iterable, cb: (value: T) => U): Iterable; +declare function filter(this: Iterable, cb: (value: T) => boolean): Iterable; +declare function reduce(this: Iterable, cb: (memo: T, value: T) => T, initial: T): T; +[1, 2, 3] + :: map(x => x * 2) + :: filter(x => x > 2) + :: reduce((x, y) => x + y, 0); diff --git a/tests/cases/conformance/parser/esnext/parser.partialApplication.esnext.1.ts b/tests/cases/conformance/parser/esnext/parser.partialApplication.esnext.1.ts new file mode 100644 index 00000000000..cda27b5a2e8 --- /dev/null +++ b/tests/cases/conformance/parser/esnext/parser.partialApplication.esnext.1.ts @@ -0,0 +1,32 @@ +// @target: esnext +// @noEmit: true +declare function f(a: number, b: number, ...c: number[]): void; +declare const o: { m(a: number, b: number, ...c: number[]): void; }; +// positional elements +f(?, 0); +f(0, ?); +f(?, ?); +o.m(?, 0); +o.m(0, ?); +o.m(?, ?); +// ordinal positional elements +f(?0, 0); +f(0, ?0); +f(?0, ?1); +f(?1, ?0); +o.m(?0, 0); +o.m(0, ?0); +o.m(?0, ?1); +o.m(?1, ?0); +// positional spread element +f(0, 1, ...); +f(0, 1, ..., 2); +o.m(0, 1, ...); +o.m(0, 1, ..., 2); +// mixed +f(?, 0, ...); +f(0, ?, ...); +f(0, 1, ..., ?0); +o.m(?, 0, ...); +o.m(0, ?, ...); +o.m(0, 1, ..., ?0); \ No newline at end of file diff --git a/tests/cases/conformance/parser/esnext/parser.pipeline.esnext.1.ts b/tests/cases/conformance/parser/esnext/parser.pipeline.esnext.1.ts new file mode 100644 index 00000000000..eea1e0636ec --- /dev/null +++ b/tests/cases/conformance/parser/esnext/parser.pipeline.esnext.1.ts @@ -0,0 +1,29 @@ +// @target: esnext +// @noEmit: true +// @lib: es2015 +// single argument +declare function f1(x: number): string; +1 |> f1; +1 |> f1(?); +(1) |> f1; +(1) |> f1(?); +// multiple argument +declare function f2(x: number, y: number): string; +(1, 2) |> f2; +(1, 2) |> f2(?, ?); +// multiple steps +declare function f3(x: string): boolean; +declare function f4(x: string, y: number): boolean; +1 |> f1 |> f3; +1 |> f1(?) |> f3; +1 |> f1 |> f3(?); +1 |> f1(?) |> f3(?); +(1 |> f1(?), 2) |> f4; +// use case +declare function map(source: Iterable, cb: (value: T) => U): Iterable; +declare function filter(source: Iterable, cb: (value: T) => boolean): Iterable; +declare function reduce(source: Iterable, cb: (memo: T, value: T) => T, initial: T): T; +[1, 2, 3] + |> map(?, x => x * 2) + |> filter(?, x => x > 2) + |> reduce(?, (x, y) => x + y, 0); \ No newline at end of file