Skip to content

Commit e1bc916

Browse files
committed
Merge branch 'master' into excess-property-checks-for-discriminated-unions
2 parents 97ee951 + 7a4c331 commit e1bc916

19 files changed

Lines changed: 971 additions & 6 deletions

src/compiler/checker.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16572,6 +16572,12 @@ namespace ts {
1657216572
return resolveUntypedCall(node);
1657316573
}
1657416574

16575+
if (isPotentiallyUncalledDecorator(node, callSignatures)) {
16576+
const nodeStr = getTextOfNode(node.expression, /*includeTrivia*/ false);
16577+
error(node, Diagnostics._0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0, nodeStr);
16578+
return resolveErrorCall(node);
16579+
}
16580+
1657516581
const headMessage = getDiagnosticHeadMessageForDecoratorResolution(node);
1657616582
if (!callSignatures.length) {
1657716583
let errorInfo: DiagnosticMessageChain;
@@ -16584,6 +16590,18 @@ namespace ts {
1658416590
return resolveCall(node, callSignatures, candidatesOutArray, headMessage);
1658516591
}
1658616592

16593+
/**
16594+
* Sometimes, we have a decorator that could accept zero arguments,
16595+
* but is receiving too many arguments as part of the decorator invocation.
16596+
* In those cases, a user may have meant to *call* the expression before using it as a decorator.
16597+
*/
16598+
function isPotentiallyUncalledDecorator(decorator: Decorator, signatures: Signature[]) {
16599+
return signatures.length && every(signatures, signature =>
16600+
signature.minArgumentCount === 0 &&
16601+
!signature.hasRestParameter &&
16602+
signature.parameters.length < getEffectiveArgumentCount(decorator, /*args*/ undefined, signature));
16603+
}
16604+
1658716605
/**
1658816606
* This function is similar to getResolvedSignature but is exclusively for trying to resolve JSX stateless-function component.
1658916607
* The main reason we have to use this function instead of getResolvedSignature because, the caller of this function will already check the type of openingLikeElement's tagName

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,10 @@
907907
"category": "Error",
908908
"code": 1328
909909
},
910+
"'{0}' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@{0}()'?": {
911+
"category": "Error",
912+
"code": 1329
913+
},
910914

911915
"Duplicate identifier '{0}'.": {
912916
"category": "Error",
@@ -3705,6 +3709,10 @@
37053709
"category": "Message",
37063710
"code": 90027
37073711
},
3712+
"Call decorator expression.": {
3713+
"category": "Message",
3714+
"code": 90028
3715+
},
37083716

37093717
"Convert function to an ES2015 class": {
37103718
"category": "Message",

src/compiler/emitter.ts

100755100644
File mode changed.

src/compiler/transformers/module/module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -861,10 +861,10 @@ namespace ts {
861861
if (original && hasAssociatedEndOfDeclarationMarker(original)) {
862862
// Defer exports until we encounter an EndOfDeclarationMarker node
863863
const id = getOriginalNodeId(node);
864-
deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true);
864+
deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), visitNode(node.expression, importCallExpressionVisitor), /*location*/ node, /*allowComments*/ true);
865865
}
866866
else {
867-
statements = appendExportStatement(statements, createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true);
867+
statements = appendExportStatement(statements, createIdentifier("default"), visitNode(node.expression, importCallExpressionVisitor), /*location*/ node, /*allowComments*/ true);
868868
}
869869

870870
return singleOrMany(statements);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* @internal */
2+
namespace ts.codefix {
3+
registerCodeFix({
4+
errorCodes: [Diagnostics._0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0.code],
5+
getCodeActions: (context: CodeFixContext) => {
6+
const sourceFile = context.sourceFile;
7+
const token = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
8+
const decorator = getAncestor(token, SyntaxKind.Decorator) as Decorator;
9+
Debug.assert(!!decorator, "Expected position to be owned by a decorator.");
10+
const replacement = createCall(decorator.expression, /*typeArguments*/ undefined, /*argumentsArray*/ undefined);
11+
const changeTracker = textChanges.ChangeTracker.fromContext(context);
12+
changeTracker.replaceNode(sourceFile, decorator.expression, replacement);
13+
14+
return [{
15+
description: getLocaleSpecificMessage(Diagnostics.Call_decorator_expression),
16+
changes: changeTracker.getChanges()
17+
}];
18+
}
19+
});
20+
}

src/services/codefixes/fixes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/// <reference path="addMissingInvocationForDecorator.ts" />
12
/// <reference path="correctQualifiedNameToIndexedAccessType.ts" />
23
/// <reference path="fixClassIncorrectlyImplementsInterface.ts" />
34
/// <reference path="fixAddMissingMember.ts" />
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5): error TS1241: Unable to resolve signature of method decorator when called as an expression.
1+
tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5): error TS1329: 'dec' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@dec()'?
22

33

44
==== tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts (1 errors) ====
@@ -7,5 +7,5 @@ tests/cases/conformance/decorators/class/method/decoratorOnClassMethod6.ts(4,5):
77
class C {
88
@dec ["method"]() {}
99
~~~~
10-
!!! error TS1241: Unable to resolve signature of method decorator when called as an expression.
10+
!!! error TS1329: 'dec' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@dec()'?
1111
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts(4,5): error TS1240: Unable to resolve signature of property decorator when called as an expression.
1+
tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts(4,5): error TS1329: 'dec' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@dec()'?
22

33

44
==== tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts (1 errors) ====
@@ -7,5 +7,5 @@ tests/cases/conformance/decorators/class/property/decoratorOnClassProperty11.ts(
77
class C {
88
@dec prop;
99
~~~~
10-
!!! error TS1240: Unable to resolve signature of property decorator when called as an expression.
10+
!!! error TS1329: 'dec' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@dec()'?
1111
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
tests/cases/compiler/dynamicImportInDefaultExportExpression.ts(3,23): error TS2307: Cannot find module './foo2'.
2+
3+
4+
==== tests/cases/compiler/dynamicImportInDefaultExportExpression.ts (1 errors) ====
5+
export default {
6+
getInstance: function () {
7+
return import('./foo2');
8+
~~~~~~~~
9+
!!! error TS2307: Cannot find module './foo2'.
10+
}
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//// [dynamicImportInDefaultExportExpression.ts]
2+
export default {
3+
getInstance: function () {
4+
return import('./foo2');
5+
}
6+
}
7+
8+
//// [dynamicImportInDefaultExportExpression.js]
9+
"use strict";
10+
exports.__esModule = true;
11+
exports["default"] = {
12+
getInstance: function () {
13+
return Promise.resolve().then(function () { return require('./foo2'); });
14+
}
15+
};

0 commit comments

Comments
 (0)