CHAPTER 2
What is wrong with the TypeScript code in Listing 3?
Code Listing 3: Simple TypeScript example
import { browser, by, element } from 'protractor'; export class AppPage { navigateTo() { return browser.get(browser.baseUrl) as Promise<any>; } getTitleText(releaseNo : number) { let subTitle = ''; switch (releaseNo) { case 1, 2: subTitle = 'Prior'; break; default: subTitle = 'Current'; } return subTitle; } } |
While there is nothing syntactically wrong with it, there are a few items that might cause problems. For example, you should declare the access level (public, private, or protected) on the method names. Using <any> somewhat defeats the purpose of a strongly typed language.
The more subtle issue is that if the releaseNo parameter contains the number 1, the code will still return Current. It is very likely this behavior isn’t what the developer expects.
This is what the lint checker is all about—looking for code that compiles, but might have issues. You can decide whether the issue being reported is something you want to address, or if it’s okay to leave the code alone.
Tip: The problem with the release number code is that the comma operator returns the last value in the list (in this example, the number 2). So, the value 1 will not trigger the first case, and will fall through to the default.
tslint will be installed as part of the Angular CLI installation, but you can also install it yourself using NPM. To install it globally, use the following command:
npm install tslint typescript -g
Although our focus in this book is Angular, you can use tslint for any TypeScript or JavaScript application.
Note: There is a similar linting tool named ESLint that some developers prefer. The TSLint tool is described in Chapter 3.
The tslint command has several options you can use when running tslint. You can use the full option name (–version, or the abbreviated single letter where indicated).
-- version -v: This returns the version number, the current major version (at the time this book was written) is 5, with a minor version of 20.
-- config -c: This option allows you to indicate where the tslint.json configuration file to use can be found. If you do not specify a location, the first tslint.json file found in the path will be used. Within an Angular project, the configuration file is typically found in the project root folder.
-- exclude -e: This option allows you to exclude a file or folder from being lint checked. The node_modules folder contains thousands of third-party libraries and should be excluded from lint checking.
-- fix: Lint checking reads your files and produces warnings and suggestions. The –- fix option will automatically fix any found suggestion, if the lint rule supports automatic fixing. This means that your source code files most likely will be updated.
Tip: Be careful using this option—remember that lint checking offers suggestions, and it could fix something that could potentially break your code.
-- init -i: Generates a default tslint.json config file in the current directory.
-- project -p: Specifies a path containing a tsconfig.json file that should be used to determine which files should be lint checked.
You can use the – help options to see the complete set of tslint command-line options.
If you run lint checking as part of a continuous integration process, you might want to check the return code to decide if checking has passed or not. tslint returns the following values:
Within an Angular project structure, you will find a file called tslint.json. This file contains the various rules you want the lint check to use when reviewing your source files.
Presets are predefined suggested sets of rules that you can use as a starting point for your lint checking. You can specify a preset using the extends keyword:
extends: “tslint.recommended”
The three defined presets are:
// added in v5.8
"ban-comma-operator": true
Since tslint.latest is updated with new minor releases, you might see additional lint errors as new features are added. You might want to use latest for your local lint testing, but use the stable recommended for any automated build pipelines.
You can visit this website to see the definition for each of the three presets.
The rules make up most of the configuration settings, and you can configure the settings to fine-tune how you want your code to be checked. For example, the default generated tslint.json file would not have caught any of the issues in Code Listing 3. In this section, we will highlight some of the various rules by category. You can see the complete list of code rules here.
The general format is the rule name (generally quoted), followed by one or more parameters. For example, the rule to prevent the comma error from being reported is:
“ban-comma-operator”: true
Some rules may have additional parameters. For example, if you wanted to restrict the number of lines allowed in a file to 150 lines, the syntax will be:
“max-file-line-count”: [true,150]
These rules are unique to TypeScript (since tslint has a JavaScript linter as well).
The TypeScript compiler will warn about unreachable code and other errors. By adding a comment with the @ts-ignore: syntax, you are telling the linter to not issue warnings. While a matter of preference, I generally like the linter to give me warnings, so I generally include this check.
Since TypeScript adds types to JavaScript, it defeats the purpose of typing if you declare your variable using the Any type. The no-any setting allows me to not accept non-typed variables.
I personally dislike magic numbers in my code. For example, what does the following code mean?
if (result_code == 413)
Of course, you could look up the HTTP code for 413, if you know the result_code variable represents a status code. But if the code read:
if (result_code == REQUEST_TOO_LARGE)
You, and people maintaining the code, would have a much easier time reading the code. For this reason, I typically turn this error rule on.
Certain code elements and statements might not be desirable in your code base, such as debugger statements and eval.
Conditional and looping statements do not always require curly braces, but in general, using them can reduce the likelihood of subtle coding errors. By setting curly to true, you can identify expressions without curly braces as errors. You can provide additional parameters to tweak behavior a bit:
The eval statement takes a string parameter and executes it as JavaScript code. This can be very dangerous, since there is no way to secure the string contents. In general, eval() should be avoided. The no-eval rule will flag as an error any use of the eval() function.
The var keyword is used to declare a variable, but the scope of the variable is to the function body, not the enclosing block. In addition, using var at the top level will create a property on the global object. In general, it is better programming practice to keep the variable scope as small as possible. The let and const statements keep the scope local to the enclosing block, and do not add the variable as a property on the global object. The no-var-keyword rule reports as an error any use of the var command to declare a variable.
Tip: TSLint can fix var declarations by using either let (variable can be edited) or const (variable value is not changed)
The maintainability rules are used to make your code easier to maintain (for you or future developers). Two rules I generally add are described in this section.
This is a code metric as to how complex (confusing) a routine appears to be. The score starts at zero, then is incremented for decision statements that can add to the control flow. A common saying is that it’s “harder to read code than to write it.” Often, developers will rewrite a piece of code, rather than trying to understand what the code is actually doing.
System design should have small, single-purpose functions with meaningful variable names, which your future self or other developers will appreciate.
You can turn complexity measurement on, and set the maximum allowed score, using the following rule:
“cyclomatic-complexity”: [true,15]
This will compute the complexity and report an error if the score exceeds 15. Scores of 1–10 indicate good, easy-to-test code. Code with scores above 20–25 become more complex and are more difficult to test. A complexity score also suggests how many tests you need to write to completely test the function. You can read more about code complexity here.
The @deprecated tag in a comment is used to indicate a function or module has been deprecated. By setting this rule to true, such code will be flagged as linting errors.
This rule controls how many classes can be defined in a file, and is set using the array structure of [true, number of classes]. I generally program one class per file, but you can use this option to set your own standards. Similarly, this is a max-lines-per-file rule, as mentioned in an example at the beginning of this chapter.
Style rules are generally applied to encourage developers to write consistent code with other developers. You can control things like comment rules, header format, and file naming.
This setting allows you to specify the casing rule (camel-case, pascal-case, kebab-case, snake-case) for all file names, or you can set specific casing rules based on file extension. The rule format is:
“file-name-casing”: [ true, “pascal-case”]
This causes all file names that are not in Pascal case to be flagged as a lint error.
You can also pass an array to use different casing by file extension:
“file-name-casing”: [ true, {“.css”: “pascal-case”,“.ts”: “camel-case”} ]
The ignore option causes certain file extension to ignore casing rules.
A style preference some developers like is to have each variable declaration on its own line. You can set this rule to true to require each variable declaration (let or const) have its own line. This would prevent statements such as:
const salesTaxRate = 0.2, incomeTaxRate = .25
Instead, requiring:
const salesTaxRate = 0.2;
const incomeTaxRate = 0.25;
There is an old saying that there are only two hard things in computer science: cache invalidation and naming things. While that may be true, and it is hard to assume everyone on the team will use meaningful variable names, we can at least use tslint to make the variable names look consistent.
The variable name rule is an array of options, controlling how variables can be named.
“variable-name”: {
“options:” [
“ban-keywords”,
“check-format”,
“allow-leading-underscore”]
}
ban-keywords prevents using various TypeScript keywords (any, number, string, boolean, undefined) for variable or parameter names.
check-format uses lower camel case names for variables and uppercase for constants.
With allowing-leading-underscore, a variable name can begin with an underscore.
You can also use allow-pascal-case and allow-snake-case to increase the allowed case rules for variables (these are added to rule check).
Hopefully, as AI improves, we can add a new rule: use sensible variable names.
The format rules generally deal with things like punctation, white space, and line length. The goal of these rules is to make the code easier to read by keeping its appearance consistent. Most of the rules in this group can be automatically fixed by the lint process.
This setting allows you to control how long an individual source code line should be. It takes an array with two values: true and the line length.
“max-line-length”: [true,100]
There are additional options to fine-tune which lines should be checked for a maximum line length, such as ignore-pattern. This allows you to specify regex patterns that, if found, cause the line length check to be ignored.
The semicolon rule determines whether semicolons should be added at the end of statements. It is set using the following syntax:
“semicolon”: [ true, “always” | “never”]
If you choose always, the semicolon must be present, even if not technically required. The never option does not allow semicolons unless they are required by the statement.
White space can improve code readability. The rule is set by defining an array with true and any number of options.
“whitespace”: [ true, “check-branch”, “check-decl”, “check-operator” ]
“check-branch” looks for white space around branching statements (if, else, while).
“check-decl” makes sure there is white space around the equals assignment, for example, taxRate = 0.25.
“check-operator” makes sure there is white space around operator tokens, for example, rate = rate + 5.
There are other settings available to tweak how white space is handled in your code.
Some of the code can be fixed as part of the lint process. The rules will have the following icon (Figure 1) attached to them on the tsLint website.
![]()
Figure 1: Has fixer
You can run a lint check with the –fix option if you want some of the errors to be automatically corrected. However, you should back up your files to be safe, just in case a “fixed item” breaks your compile. I would suggest running the fixer option on a single file at a time, and only if there are too many items to fix manually.
Code Listing 4 shows our routing modules and the lint errors.
Code Listing 4: Lint check code
const routes: Routes = [ { path: 'withdrawal', component: WithdrawalComponent }, { path: 'deposit', component: DepositComponent }, { path: "transfer", component: TransferComponent } ];
@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { public feePercent = 0.025 } |
Two errors are reported: the double quotes on the transfer path and the missing semicolon in the feePercent assignment. (There is also third error, trailing white space, which is hard to illustrate in a book format.)
Code Listing 5 shows the code snippet after running the TSLint –fix option on this file.
Code Listing 5: Automatically fixed rules
const routes: Routes = [ { path: 'withdrawal', component: WithdrawalComponent }, { path: 'deposit', component: DepositComponent }, { path: 'transfer', component: TransferComponent } ];
@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { public feePercent = .025; |
Notice that the quotes have been changed on the transfer path, and the semicolon has been added after the feePercent assignment statement. The trailing white space has also been corrected.
Fixers are more common in the style and maintainability sections, but still should be run with care, since your source code will be modified, and the process will not back up your file.
Linting is a great, quick tool to find those little gotchas that the compiler is okay with but might not be what you wanted. It can also be very useful in a team environment to ensure the code is consistently styled and maintainable. I would recommend requiring that lint checks pass prior to all code check-ins, and including lint checking in your continuous integration environment.