diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f9cca936347..4baec35bfbd 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3243,6 +3243,7 @@ namespace ts { break; case SyntaxKind.BindExpression: + case SyntaxKind.BindToExpression: transformFlags |= TransformFlags.AssertESNext; break; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d1417a9383a..173c40abb65 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12217,6 +12217,11 @@ namespace ts { } function checkBindExpression(node: BindExpression): Type { + const signatures = getResolvedPartialSignatures(node); + return createTypeFromSignatures(map(signatures, getSignatureWithoutThis)); + } + + function checkBindToExpression(node: BindToExpression): Type { checkNonNullExpression(node.expression); const signatures = getResolvedPartialSignatures(node); return createTypeFromSignatures(map(signatures, getSignatureWithoutThis)); @@ -12376,7 +12381,9 @@ namespace ts { callIsIncomplete = !!templateLiteral.isUnterminated; } } - else if (node.kind === SyntaxKind.Decorator || node.kind === SyntaxKind.BindExpression) { + else if (node.kind === SyntaxKind.Decorator + || node.kind === SyntaxKind.BindExpression + || node.kind === SyntaxKind.BindToExpression) { typeArguments = undefined; argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature); } @@ -12599,8 +12606,8 @@ namespace ts { * Returns the this argument in calls like x.f(...) and x[f](...). Undefined otherwise. */ function getThisArgumentOfCall(node: CallLikeExpression): LeftHandSideExpression { - if (node.kind === SyntaxKind.CallExpression) { - const callee = (node).expression; + if (node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.BindExpression) { + const callee = node.expression; if (callee.kind === SyntaxKind.PropertyAccessExpression) { return (callee as PropertyAccessExpression).expression; } @@ -12608,7 +12615,7 @@ namespace ts { return (callee as ElementAccessExpression).expression; } } - else if (node.kind === SyntaxKind.BindExpression) { + else if (node.kind === SyntaxKind.BindToExpression) { return node.expression; } } @@ -12633,7 +12640,9 @@ namespace ts { }); } } - else if (node.kind === SyntaxKind.Decorator || node.kind === SyntaxKind.BindExpression) { + else if (node.kind === SyntaxKind.Decorator + || node.kind === SyntaxKind.BindToExpression + || node.kind === SyntaxKind.BindExpression) { // For a decorator, we return undefined as we will determine // the number and types of arguments for a decorator using // `getEffectiveArgumentCount` and `getEffectiveArgumentType` below. @@ -12712,7 +12721,8 @@ namespace ts { return 3; } } - else if (node.kind === SyntaxKind.BindExpression) { + else if (node.kind === SyntaxKind.BindToExpression + || node.kind === SyntaxKind.BindExpression) { return signature.minArgumentCount; } else { @@ -12899,7 +12909,8 @@ namespace ts { if (node.kind === SyntaxKind.Decorator) { return getEffectiveDecoratorArgumentType(node, argIndex); } - else if (node.kind === SyntaxKind.BindExpression) { + else if (node.kind === SyntaxKind.BindToExpression + || node.kind === SyntaxKind.BindExpression) { return unknownType; } else if (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) { @@ -12917,6 +12928,7 @@ namespace ts { function getEffectiveArgument(node: CallLikeExpression, args: Expression[], argIndex: number) { // For a decorator or the first argument of a tagged template expression we return undefined. if (node.kind === SyntaxKind.Decorator || + node.kind === SyntaxKind.BindToExpression || node.kind === SyntaxKind.BindExpression || (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression)) { return undefined; @@ -12943,16 +12955,9 @@ namespace ts { } function resolveCall(node: CallLikeExpression, signatures: Signature[], partialSignaturesOutArray: Signature[], candidatesOutArray: Signature[], headMessage?: DiagnosticMessage): Signature { - const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression; - const isDecorator = node.kind === SyntaxKind.Decorator; - const isPipeline = node.kind === SyntaxKind.BinaryExpression; - const isBind = node.kind === SyntaxKind.BindExpression; - let typeArguments: TypeNode[]; - - if (!isTaggedTemplate && !isDecorator && !isPipeline && !isBind) { - typeArguments = (node).typeArguments; - + if (node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression) { + 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); @@ -12984,10 +12989,12 @@ namespace ts { // For a decorator, no arguments are susceptible to contextual typing due to the fact // decorators are applied to a declaration by the emitter, and not to an expression. let excludeArgument: boolean[]; - if (!isDecorator && !isBind) { + if (node.kind === SyntaxKind.CallExpression || + node.kind === SyntaxKind.NewExpression || + node.kind === SyntaxKind.TaggedTemplateExpression) { // We do not need to call `getEffectiveArgumentCount` here as it only // applies when calculating the number of arguments for a decorator. - for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) { + for (let i = node.kind === SyntaxKind.TaggedTemplateExpression ? 1 : 0; i < args.length; i++) { if (isContextSensitive(args[i])) { if (!excludeArgument) { excludeArgument = new Array(args.length); @@ -13065,8 +13072,7 @@ namespace ts { checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true); } else if (candidateForTypeArgumentError) { - if (!isTaggedTemplate && !isDecorator && typeArguments) { - const typeArguments = (node).typeArguments; + if (typeArguments) { checkTypeArguments(candidateForTypeArgumentError, typeArguments, map(typeArguments, getTypeFromTypeNode), /*reportErrors*/ true, headMessage); } else { @@ -13485,6 +13491,26 @@ namespace ts { } function resolveBindExpression(node: BindExpression, partialSignaturesOutArray: Signature[], candidatesOutArray?: Signature[]): Signature { + const funcType = checkExpression(node.expression); + const apparentType = getApparentType(funcType); + if (apparentType === unknownType) { + 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); + } + + if (!callSignatures.length) { + return resolveErrorCall(node); + } + + return resolveCall(node, callSignatures, partialSignaturesOutArray, candidatesOutArray); + } + + function resolveBindToExpression(node: BindToExpression, partialSignaturesOutArray: Signature[], candidatesOutArray?: Signature[]): Signature { const funcType = checkExpression(node.targetExpression); const apparentType = getApparentType(funcType); if (apparentType === unknownType) { @@ -13518,6 +13544,8 @@ namespace ts { return resolvePipelineExpression(node, candidatesOutArray); case SyntaxKind.BindExpression: return resolveBindExpression(node, partialSignaturesOutArray, candidatesOutArray); + case SyntaxKind.BindToExpression: + return resolveBindToExpression(node, partialSignaturesOutArray, candidatesOutArray); } Debug.fail("Branch in 'resolveSignature' should be unreachable."); } @@ -15310,6 +15338,8 @@ namespace ts { return checkIndexedAccess(node); case SyntaxKind.BindExpression: return checkBindExpression(node); + case SyntaxKind.BindToExpression: + return checkBindToExpression(node); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return checkCallExpression(node); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 675de53bd1e..15dc9382477 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -618,6 +618,8 @@ namespace ts { return emitElementAccessExpression(node); case SyntaxKind.BindExpression: return emitBindExpression(node); + case SyntaxKind.BindToExpression: + return emitBindToExpression(node); case SyntaxKind.CallExpression: return emitCallExpression(node); case SyntaxKind.NewExpression: @@ -1081,12 +1083,17 @@ namespace ts { write("]"); } - function emitBindExpression(node: BindExpression) { + function emitBindToExpression(node: BindToExpression) { emitExpression(node.expression); write("::"); emitExpression(node.targetExpression); } + function emitBindExpression(node: BindExpression) { + write("::"); + emitExpression(node.expression); + } + function emitCallExpression(node: CallExpression) { emitExpression(node.expression); emitTypeArguments(node, node.typeArguments); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 43be7cc7b58..722c71c5323 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -162,8 +162,10 @@ namespace ts { return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).argumentExpression); case SyntaxKind.BindExpression: - return visitNode(cbNode, (node).expression) || - visitNode(cbNode, (node).targetExpression); + return visitNode(cbNode, (node).expression); + case SyntaxKind.BindToExpression: + return visitNode(cbNode, (node).expression) || + visitNode(cbNode, (node).targetExpression); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return visitNode(cbNode, (node).expression) || @@ -2781,6 +2783,7 @@ namespace ts { case SyntaxKind.LessThanToken: case SyntaxKind.AwaitKeyword: case SyntaxKind.YieldKeyword: + case SyntaxKind.ColonColonToken: // Yield/await always starts an expression. Either it is an identifier (in which case // it is definitely an expression). Or it's a keyword (either because we're in // a generator or async function, or in strict mode (or both)) and it started a yield or await expression. @@ -3433,6 +3436,13 @@ namespace ts { return finishNode(node); } + function parseBindExpression() { + const node = createNode(SyntaxKind.BindExpression); + nextToken(); + node.expression = parseMemberExpressionOrHigher(); + return finishNode(node); + } + function parseDeleteExpression() { const node = createNode(SyntaxKind.DeleteExpression); nextToken(); @@ -4030,7 +4040,7 @@ namespace ts { } if (parseOptional(SyntaxKind.ColonColonToken)) { - const bindExpression = createNode(SyntaxKind.BindExpression, expression.pos); + const bindExpression = createNode(SyntaxKind.BindToExpression, expression.pos); bindExpression.expression = expression; bindExpression.targetExpression = parseMemberExpressionOrHigher(); expression = finishNode(bindExpression); @@ -4179,6 +4189,8 @@ namespace ts { break; case SyntaxKind.TemplateHead: return parseTemplateExpression(); + case SyntaxKind.ColonColonToken: + return parseBindExpression(); } return parseIdentifier(Diagnostics.Expression_expected); diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index f4b666e5f78..08cb3c83ac0 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -73,6 +73,8 @@ namespace ts { return visitOperatorExpression(node as OperatorExpression); case SyntaxKind.BindExpression: return visitBindExpression(node as BindExpression); + case SyntaxKind.BindToExpression: + return visitBindToExpression(node as BindToExpression); default: return visitEachChild(node, visitor, context); } @@ -545,6 +547,28 @@ namespace ts { } function visitBindExpression(node: BindExpression) { + const operand = visitNode(node.expression, visitor, isExpression); + if (operand.kind === SyntaxKind.PropertyAccessExpression + || operand.kind === SyntaxKind.ElementAccessExpression) { + const { target, thisArg } = createCallBinding(operand, hoistVariableDeclaration); + return createFunctionBind( + target, + thisArg, + [], + node + ) + } + else { + return createFunctionBind( + operand, + createNull(), + [], + node + ); + } + } + + function visitBindToExpression(node: BindToExpression) { const thisArg = createTempVariable(context.hoistVariableDeclaration); return createComma( createAssignment( diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2b82ed51a8c..41e1692523d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -234,7 +234,7 @@ namespace ts { ObjectLiteralExpression, PropertyAccessExpression, ElementAccessExpression, - BindExpression, + BindToExpression, CallExpression, NewExpression, TaggedTemplateExpression, @@ -246,6 +246,7 @@ namespace ts { TypeOfExpression, VoidExpression, AwaitExpression, + BindExpression, PrefixUnaryExpression, PostfixUnaryExpression, BinaryExpression, @@ -1419,12 +1420,17 @@ namespace ts { | SuperElementAccessExpression ; - export interface BindExpression extends MemberExpression { - kind: SyntaxKind.BindExpression; + export interface BindToExpression extends MemberExpression { + kind: SyntaxKind.BindToExpression; expression: LeftHandSideExpression; targetExpression: MemberExpression; } + export interface BindExpression extends PrimaryExpression { + kind: SyntaxKind.BindExpression; + expression: MemberExpression; + } + export interface CallExpression extends LeftHandSideExpression, Declaration { kind: SyntaxKind.CallExpression; expression: LeftHandSideExpression; @@ -1456,7 +1462,7 @@ namespace ts { template: TemplateLiteral; } - export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | PipelineExpression | BindExpression; + export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | PipelineExpression | BindExpression | BindToExpression; export interface PositionalElement extends Expression { kind: SyntaxKind.PositionalElement; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index af97bb56f2f..88e66420bef 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3950,6 +3950,7 @@ namespace ts { function isMemberExpressionKind(kind: SyntaxKind): boolean { return kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression + || kind === SyntaxKind.BindToExpression || kind === SyntaxKind.BindExpression || kind === SyntaxKind.JsxElement || kind === SyntaxKind.JsxSelfClosingElement