What are these strict compiler options in TypeScript: Part 1

Kyrylo Reznykov
Jan 9 · 4 min read
When TypeScript compiler runs in strict mode

In my previous article, I briefly mentioned that every serious TypeScript project should have enabled --strict compiler option, but I didn’t provide any additional details about what it does. The official TypeScript handbook page is quite useless and doesn’t have many details as well. So to close that gap I am going to write about the most important options from the strict family in details.


- - noImplicitAny

This option prevents you from adding an accidental any type in your codebase. To focus all your attention on the benefit of that flag, I will show you a code example that is as simple as possible. It might look silly, but that kind of problem is real and often happens in massive codebases without quality test coverage. So let’s imagine that you have a common isOddNumber function where you forgot to add type annotations:

const isOddNumber = (num) => (num % 2) !== 0;
const nums = [1, 2, 3];
console.log(isOddNumber(nums[0])); // => prints true
console.log(isOddNumber(nums[1])); // => prints false
// oops there is a typo in our code, we planned to pass nums[2];
console.log(isOddNumber(nums)) // => prints true
// funny that it would work as intended with 1 element in array
console.log(isOddNumber([3])) // => prints true

Even though that code will compile successfully and will not produce a runtime exception, the 3rd console.log statement has a nasty bug that can live in your app for a long time without attention. The culprit is any type. Even if you don’t see it in the code explicitly, it is there. That is because you gave TypeScript compiler the right to add the any type every time when it can’t figure out anything better. So at the time of compilation, your first two lines will be transformed to something like that:

const isOddNumber = (num: any): boolean => (num % 2) !== 0;
const nums: number[] = [1, 2, 3];

Here the TypeScript compiler inferred two types correctly without a problem (for the array and the return value), but the input value of isOddNumber function is an impossible task for it. However, if your compiler would run with --noImplicitAny option enabled it would fail to build and print the following error message in your console:

ERROR: Parameter 'num' implicitly has an 'any' type

Awesome, now you, as a developer, have control whether or not to write a number type for the num parameter or to declare it as any.


- - noImplicitThis

Because JavaScript is a very dynamic language with the ability to call functions passing arbitrary this context, TypeScript can’t do correct type inference in a lot of cases. So if we don’t enable that option TypeScript assumes that you don’t care about the type of this keyword and will use any type for it until you specify something else.

function foo(hello: string) {
console.log(hello + ' ' + this.fullName);
}
foo.call({ fullName: 'Kirill' }, 'hi'); // => prints "hi Kirill"const x = { fullName: 'Max', foo: foo }
x.foo("hello"); // => prints "hello Max"
foo.call(10, "hey"); // no errors, prints "hey undefined"
foo('hi'); // runtime error, but no compile time error

To show TypeScript that you are concerned about the type of this keyword and to prevent it from defaulting to any whenever you forget to declare it, we need to enable --noImplicitThis option. After that, you will have an error at build time until you provide a type for this. To define this type we need to add a first pseudo parameter with a name this:

interface User { fullName: string }function foo(this: User, hello: string) {
console.log(hello + ' ' + this.fullName);
}
foo.call({ fullName: 'Kirill' }, 'hi'); // => prints "hi Kirill"const x = { fullName: 'Max', foo: foo }
x.foo("hello"); // => prints "hello Max"
foo.call(10, "hey"); // compile error, '10' is not a type of 'User'
foo('hi'); // compile error, 'void' is not assignable to 'User'.

Perfect, now the type of this keyword will never be any in your application until a developer decides that it is necessary for a particular function.


- - strictBindCallApply

That option asks TypeScript compiler to check argument types when you use bind, call or apply. By default, it doesn’t care about types in such context leaving you more opportunities for a mistake:

function bar(a: string, b: number) {
console.log(a, b);
}
// no errors
bar('cool', 10);
// error since we didn't pass the second argument
bar('cool');
// no errors
bar.call(null, 'cool');
bar.call(null, true, false);
bar.bind(null)();
bar.bind(null, 'cool')(10);

As soon as we enable --strictBindCallApply option we will see completely different results during compilation:

// no errors
bar('cool', 10);
// error since we didn't pass the second argument
bar('cool');
// error since we didn't pass the second argument
bar.call(null, 'cool');
// error since types are not matching
bar.call(null, true, false);
// error since we didn't pass arguments at all
bar.bind(null)();
// no errors type and amount of parameters mathes
bar.bind(null, 'cool')(10);

Conclusion

I hope that the article was able to explain to you how important are these options. However, that is not an exhaustive list and there is a second part where I have written about the remaining strict options. Don’t miss it.

Kyrylo Reznykov

Written by

I am a skillful developer who has experience in mobile development, enterprise java applications, and ultra-modern UI interfaces with offline-first design.

JavaScript in Plain English

Learn the web's most important programming language.

More From Medium

More from JavaScript in Plain English

More from JavaScript in Plain English

How to Secure Your API With JSON Web Tokens

More from JavaScript in Plain English

More from JavaScript in Plain English

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade