How To Configure tsconfig.json: TypeScript Strict options
TypeScript is not just a superset of JavaScript with static types. It is also a quite configurable tool that can be used for different types of projects. One parameter or group of parameters that can be configured is strict. If you are not familiar with strict mode and why you should use it for a new project then check out the post What Is Strict Mode In TypeScript, Why And When You Should Use It?. In this post I focus more on a practical side of this topic.
TypeScript’s strict mode parameter can be configurated as several individual parameters for each specific case of type checking. So, basically, if you set the parameter strict to true in tsconfig.json it means that all these strict options are set to true.
List of strict options:
- useUnknownInCatchVariables (new)
- noImplicitAny
- strictNullChecks
- strictFunctionTypes
- strictBindCallApply
- strictPropertyInitialization
- noImplicitThis
- alwaysStrict
Let’s explore each strict option in practice.
TypeScript Strict options in tsconfig.json: useUnknownInCatchVariables
This option was introduced in TypeScript 4.4.
The problem is that when we use construction try catch the type of variable error in catch is any:
It increases the potential risk of errors and application malfunction. The option useUnknownInCatchVariables solves this problem.
If you set option useUnknownInCatchVariables to true
then variable error in every try catch in your code base will have type unknown
:
|
|
You can also use type Error for error variable:
|
|
TypeScript Strict options in tsconfig.json: noImplicitAny
Let’s start with option noImplicitAny.
In the main.ts file (or whatever file you want) let’s create a simple function:
|
|
If you run tsc
command you will see that TypeScript successfully compiles the code because there is no error.
Now, set the options in configuration file tsconfig.json in your project:
|
|
If you’re writing your code in an editor like Visual Studio Code or some IDE you probably already see that something is wrong with parameter data in the function. Let’s run TypeScript compiler tsc
and see what it will tell us.
TypeScript compiler will print something like this:
|
|
So, if you set the option noImplicitAny to true
, TypeScript won’t allow us to write functions with parameters without types of parameters. The thing is that TypeScript doesn’t know what type of the parameter data is and it doesn’t infer because there is no information in the code about that value should be there.
You need to set some type to avoid this TypeScript error. For example, I’ll specify type string for the data:
|
|
Also, if your parameter is not required, you can specify the default value of the parameter. And there is the thing: if you set the default value of the parameter then you won’t need to specify the type. In that case, TypeScript will understand what type of the parameter is by Type inference.
An example. The default value of the parameter is empty string so type of the parameter is string:
|
|
TypeScript Strict options in tsconfig.json: Why Should noImplicitAny Be Enabled?
By setting the option noImplicitAny to true
, TypeScript forces you to write safer code. How? The problem with ignorance of the type of the parameter is that you can manipulate the value in the code by methods that can’t work with this value. For example, inside the function printData you can use method .toLowerCase that works with type string. Your colleague (or even you!) can use the function printData somewhere in the future. Because you don’t know what the type of the parameter data is, you probably can put the number value to this parameter.
|
|
The code above will successfully be compiled by tsc
because there are no errors from the TypeScript perspective. But when you will run the program in the Web browser or by Node as in our case, you will see that program falls:
|
|
You can avoid this error before executing the code by specifying the type of the parameter. The TypeScript’s option noImplicitAny won’t allow you to escape from specifying the type in the new code.
TypeScript Strict options in tsconfig.json: strictNullChecks
Source code of this example is available on GitHub
This parameter obligates us to make a check of the variable existing. For example, let’s say we have an array of some object. This data is available in a code of app from JSON file:
src/inventory.json
|
|
In some modules, we have a code where this JSON file is imported and used as a database. The app is simple: it asks the user to type the name of the item from inventory and then if this item exists the program will print information about it.
src/main.ts
|
|
If you run this program by npm run dev
, type any name and one of three item’s names (sword, bow, shield) the program will run as it should. The problems begin when you type the name of the item that does not exist in the inventory. If you try this, you’ll see something like this:
|
|
All we need to do to fix this problem is to add the code that checks the variable existing before using it for printing the result. But the point is that TypeScript should highlight that we need to fix the potential problem. To do it just set option strictNullChecks to true:
tsconfig.json
|
|
Now, let’s run npm run dev
and see that happens:
|
|
Great! Now we have information about where the problem is. Just add checking the variable foundItem:
|
|
TypeScript Strict options in tsconfig.json: strictNullChecks and Exclamation mark
Master Go (Golang) with Weekly Tips, Tutorials and Exclusive Courses
Keep your Go (Golang) skills sharp and up-to-date with my exclusive newsletter. Subscribe now to get:
- Weekly tips and tricks
- In-depth tutorials
- Exclusive courses
- Industry insights and best practices
Join growing community of Go (Golang) developers and take your skills to the next level.
Latest IssuesYou can also use “!” in such a case when you are sure that found item or element exist. Let’s see an example:
|
|
In this case, a user is not typing the name of the inventory item but type an option number offered by the app. Because the code checks that the user typed option number that surely exists (the line if (!itemsIds.includes(option)) {
) we don’t need to manually check that variable foundItem has data inside. But TypeScript will tell us that we need to check this variable because Object is possibly ‘undefined’. To avoid this highlight we can use exclamation mark:
|
|
It tells TypeScript that we are totally sure that foundItem is not undefined or null. After that you can run the app it will work correctly.
I recommend not use exclamation mark very often because it can expand the count of potential mistakes in the future. Use it only in case when you are totally sure that some data exists.
TypeScript Strict options in tsconfig.json: strictBindCallApply
Source code of this example is available on GitHub
The next option is not so useful nowadays since we don’t need to use bind() and related methods much often in modern JavaScript. But anyway, if you need to use bind(), call(), or apply() then this option might be useful for you.
The example is unusual but you may come across this in existing projects with an old version of ECMAScript (where arrow functions are not available or their support is disabled for some reason). This function creates an object of a non-player character. You can start the dialog with this character (in our example it starts automatically after running the app) but the character is busy right now so it answers later (after 2 sec):
|
|
Let’s create a merchant in main module:
|
|
Now, if you run the program and type your name and level (for example, 10) and then answer “yes” in dialog (type “1”) when you see something goes wrong with your level:
|
|
Typical problem with string
and number
values in JavaScript. Notice that in createMerchant in method startDialog a parameter level has type string
but in function greeting the parameter caller has field level with type number
. But we don’t have any type checking errors after running tsc. We should tell TypeScript to check parameters of function that called by bind() (call(), apply()). This is what option strictBindCallApply is for.
tsconfig.json
|
|
Now, if you run the program you will see that TypeScript highlights the problem with different types of field level in function createMerchant:
|
|
TypeScript Strict options in tsconfig.json: strictFunctionTypes
This option is intended for quite specific cases. If this option was set to true then TypeScript will not allow you to use a function in a case when types of parameters of this function are not the same as parameter’s types in specified type.
An example:
|
|
In this case, if options are enabled, tsc will return an error message after running:
|
|
Theoretically, in this case you could specify the parameter id as a number and call function logTransaction like that: logTransaction(parseInt(transactionId))
. But still, you will have a type-checking error because you cannot use method trim() for a number value.
Anyway, is good to know what specific options are needed if you enabled strict mode in your project.
TypeScript Strict options in tsconfig.json: noImplicitThis
You might know that JavaScript has a quite important nuance with the variable “this”. Let’s say, you have a method that prints a value of an object’s field. If you wrote this method as function declaration then will lose “this” of an object where the method is specified. I would say that it’s one of the famous “features” of JavaScript and Internet has tons of materials about this.
Here is an example:
|
|
After running npm run dev
you will see that it throws an error:
|
|
Now, let’s set option noImplicitThis in configuration file:
|
|
After that TypeScript will highlight an error in the code:
|
|
|
|
By doing so we can fix the problem before running an application. One of a solution, in this case, is using an arrow function:
|
|
When you change the nested function to arrow one TypeScript will stop highlight this line as an error. After running npm run dev
you will see that the program works correctly.
TypeScript Strict options in tsconfig.json: strictPropertyInitialization
The next option is directly related to classes in JavaScript and TypeScript. In TypeScript, you can specify the properties of the class and also their types. Here is an example.
Let’s say we have a special class for game characters:
|
|
Now, in the main module we create a character’s object. The character should greetings the player:
|
|
If you run this small example, you will see:
|
|
I guess we didn’t give a name to the traveler! Okay, we made a mistake in the code. It’s not a big deal. The real problem is that TypeScript didn’t say anything about it! Notice that constructor
of class Character is empty. But also there is no highlighted error or warning. We don’t have a specific syntax like required name: string
in TypeScript to declare that properties name and level are required for initialization in the class Character. However, we can enable option strictPropertyInitialization and after that TypeScript compiler will tell us that we didn’t initialize properties name and level in the constructor method of class Character.
An option strictPropertyInitialization can be enabled only if option strictNullChecks is enabled too.
|
|
And after that we run tsc
and see:
|
|
|
|
This is exactly what we need. Now, let’s fix the problem:
|
|
And don’t forget to give a name for the traveler in main module!
TypeScript Strict options in tsconfig.json: alwaysStrict
If you set the option alwaysStrict to true
then TypeScript will parse your code in ECMAScript Strict mode and put “use strict” in each source file. If you are not familiar with ECMAScript Strict mode then check out article on MDN about it.
Conclusions
When you have already learned what errors can be prevented by TypeScript’s strict options you may exclaim “It can be fixed by a few lines of code. Just add a checking of variable’s existing before print it. What’s the big deal?” and you’ll be right. But, it’s just a synthetic example to demonstrate the problem that can be solved by strict options. In reality, it could be one small part of a huge project with hundreds of files and thousands of lines of code. You cannot keep track of everything and you shouldn’t. You also can make a typo or forget about doing a check because you can’t concentrate after last night’s party. It can also happen to your new colleague who hasn’t completely figured out the codebase yet.
The point is to delegate solving errors that are related to types of variables to tools like TypeScript.