Skip to content

Commit 020c038

Browse files
committed
new rule 'const-parameters'
Fixes: #34
1 parent 4ed70fa commit 020c038

File tree

6 files changed

+162
-1
lines changed

6 files changed

+162
-1
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Now configure some of the new rules.
4646

4747
Rule | Description
4848
---- | ----
49+
[const-parameters](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/const-parameters.md) | Declare parameters as `const` with JsDoc `/** @const */`
4950
[early-exit](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/early-exit.md) | Recommends to use an early exit instead of a long `if` block.
5051
[ext-curly](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/ext-curly.md) |Enforces where to consistently use curly braces where not strictly necessary.
5152
[naming-convention](https://github.com/ajafff/tslint-consistent-codestyle/blob/master/docs/naming-convention.md) | Fine grained configuration to enfoce consistent naming for almost everything. E.g. variables, functions, classes, methods, parameters, enums, etc.

docs/const-parameters.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## ext-curly
2+
3+
Disallows reassigning parameters that are considered constants. This rule is similar to tslint's [`no-parameter-reassignent`](https://palantir.github.io/tslint/rules/no-parameter-reassignment/) but allows you to explicitly declare which parameter is a constant with JsDoc `/** @const */`
4+
5+
```ts
6+
function fn(/**@const*/foo, bar) {
7+
foo++; // error on this line
8+
bar++; // no error
9+
}
10+
11+
class C {
12+
constructor(/** @const */ public foo) {
13+
foo++; // error on this line
14+
this.foo++; // no error on this line, because only parameters are checked by this rule
15+
}
16+
}
17+
18+
function fn(/**@constant*/foo) { // also works with @constant tag
19+
foo++; // error on this line
20+
}
21+
22+
function fn({ // also works with destructured parameters
23+
/**@const*/ foo,
24+
baz: /**@const*/ bar,
25+
}) {
26+
foo++; // error on this line
27+
bar++; // error on this line
28+
}
29+
```

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"license": "MIT",
3232
"dependencies": {
3333
"tslib": "^1.7.1",
34-
"tsutils": "^2.8.2"
34+
"tsutils": "^2.11.0"
3535
},
3636
"devDependencies": {
3737
"coveralls": "^2.13.0",

rules/constParametersRule.ts

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @license
3+
* Copyright 2017 Palantir Technologies, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { collectVariableUsage, getDeclarationOfBindingElement, isReassignmentTarget, getJsDoc, parseJsDocOfNode } from 'tsutils';
19+
import * as ts from 'typescript';
20+
import * as Lint from 'tslint';
21+
22+
export class Rule extends Lint.Rules.AbstractRule {
23+
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
24+
return this.applyWithFunction(sourceFile, walk);
25+
}
26+
}
27+
28+
function walk(ctx: Lint.WalkContext<void>): void {
29+
collectVariableUsage(ctx.sourceFile).forEach((variable, identifier) => {
30+
if (!isParameter(identifier.parent!) || !isConst(identifier, ctx.sourceFile))
31+
return;
32+
for (const use of variable.uses)
33+
if (isReassignmentTarget(use.location))
34+
ctx.addFailureAtNode(use.location, `Cannot reassign constant parameter '${identifier.text}'.`);
35+
});
36+
}
37+
38+
function isParameter(node: ts.Node): boolean {
39+
switch (node.kind) {
40+
case ts.SyntaxKind.Parameter:
41+
return true;
42+
case ts.SyntaxKind.BindingElement:
43+
return getDeclarationOfBindingElement(<ts.BindingElement>node).kind === ts.SyntaxKind.Parameter;
44+
default:
45+
return false;
46+
}
47+
}
48+
49+
function isConst(name: ts.Identifier, sourceFile: ts.SourceFile) {
50+
if (name.parent!.kind === ts.SyntaxKind.Parameter)
51+
return getJsDoc(name.parent!, sourceFile).some(jsDocContainsConst);
52+
// destructuring
53+
return parseJsDocOfNode(name, true, sourceFile).some(jsDocContainsConst);
54+
}
55+
56+
function jsDocContainsConst(jsDoc: ts.JSDoc): boolean {
57+
if (jsDoc.tags !== undefined)
58+
for (const tag of jsDoc.tags)
59+
if (tag.tagName.text === 'const' || tag.tagName.text === 'constant')
60+
return true;
61+
return false;
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
function fn(foo) {
2+
foo++;
3+
}
4+
function fn(/**@constant*/foo) {
5+
foo++;
6+
~~~ [err % ('foo')]
7+
}
8+
function fn(/** @const */ foo, bar) {
9+
foo++;
10+
~~~ [err % ('foo')]
11+
bar++;
12+
}
13+
function fn(/** @const */ foo, bar) {
14+
bar = foo;
15+
}
16+
function fn(/**@const some text*/foo) {
17+
foo++;
18+
~~~ [err % ('foo')]
19+
}
20+
function fn({/**@const*/foo}) {
21+
foo++;
22+
~~~ [err % ('foo')]
23+
}
24+
function fn({bar: /**@const*/foo}) {
25+
foo++;
26+
~~~ [err % ('foo')]
27+
}
28+
function fn({/**@const*/foo, bar}) {
29+
foo++;
30+
~~~ [err % ('foo')]
31+
bar++;
32+
}
33+
class Clazz {
34+
constructor(/**@const*/foo, /**@const*/ private bar, /**@const*/ readonly baz) {
35+
foo++;
36+
~~~ [err % ('foo')]
37+
bar++;
38+
~~~ [err % ('bar')]
39+
baz++;
40+
~~~ [err % ('baz')]
41+
this.bar++;
42+
this.baz++;
43+
}
44+
}
45+
function fn(
46+
/** some description */
47+
/** @constant */
48+
foo,
49+
) {
50+
foo++;
51+
~~~ [err % ('foo')]
52+
}
53+
function fn(/** foo */foo) {
54+
foo++;
55+
}
56+
function fn(/**const*/foo) {
57+
foo++;
58+
}
59+
function fn(/**@param*/foo) {
60+
foo++;
61+
}
62+
63+
[err]: Cannot reassign constant parameter '%s'.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"rulesDirectory": "../../../../rules",
3+
"rules": {
4+
"const-parameters": true
5+
}
6+
}

0 commit comments

Comments
 (0)