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
:
{
"compilerOptions": {
// ...
"useUnknownInCatchVariables": true
}
}
You can also use type Error for error variable:
try {
// some code
} catch (e) {
if (e instanceof Error) {
console.error(e.message);
}
}
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:
function printData(data) {
console.log(data);
}
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:
{
"compilerOptions": {
"noImplicitAny": true
}
}
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:
error TS7006: Parameter 'data' implicitly has an 'any' type.
4 function printData(data) {
~~~~
Found 1 error.
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:
function printData(data: string) {
console.log(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:
function printData(data = "") {
console.log(data);
}
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.
function printData(data) {
console.log(data.toLowerCase());
}
async function main() {
printData(10);
}
main();
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:
node dist/main.js
/ts-node-sample/dist/main.js:13
console.log(data.toLowerCase());
^
TypeError: data.toLowerCase is not a function
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
[
{
"id": "1",
"name": "sword",
"level": "10",
"icon": "🗡"
},
{
"id": "2",
"name": "bow",
"level": "7",
"icon": "🏹"
},
{
"id": "3",
"name": "shield",
"level": "5",
"icon": "🛡"
}
]
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
import { createQuestioner } from "./createQuestioner";
import { greeting } from "./greeting";
import inventory from "./inventory.json";
async function main() {
try {
const questioner = createQuestioner();
const username = await questioner.ask("Type your username: ");
greeting(username);
const itemName = await questioner.ask(
"Type the name of the inventory item: "
);
const foundItem = inventory.find((item) => item.name === itemName);
console.log(
`You've chosen an item: ${foundItem.icon} ${foundItem.name} (lvl ${foundItem.level})`
);
questioner.finishUp();
} catch (e) {
console.error(e);
}
}
main();
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:
❯ npm run dev
> tsc-intro@1.0.0 dev
> tsc && node dist/main.js
Type your username: byteski
Hello, @byteski!
Type the name of the inventory item: spear
TypeError: Cannot read property 'icon' of un