Everyday TypeScript: Strict Compiler Flags
Welcome to the Strict Compiler Flags lesson!
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
Compilers tend to have many optional features and TypeScript is no exception. This lesson covers the most important compiler option, in our opinion:
strict.When running the TypeScript compiler at the command line, we can enable strict mode with
tsc --strict. But most teams will enable it by putting"strict": truein their "tsconfig.json" file.In this course, we always have
"strict": trueenabled. Many of the type errors that we've already seen came from strict mode. In each of those cases, disabling strict mode would have allowed us to introduce bugs.The
strictoption is a shorthand for several other strictness-related options. Enablingstrictenables all of the sub-options, which is easier than listing each of them separately in our "tsconfig.json". To give you a sense of what these options do, we'll go through some examples.We still have strict mode enabled in this lesson, so TypeScript will still catch our mistakes here. But with strict mode disabled, each of these mistakes would be allowed.
Option 1:
noImplicitAny. With this option enabled, we're forced to provide explicit types for every function parameter. If we leave the types off, that's a type error.>
// Note the lack of types for the function parameters.function add(x, y) {return x + y;}Result:
type error: Parameter 'x' implicitly has an 'any' type.
Without
noImplicitAny, bothxandywould implicitly have the typeany. TypeScript would also infer theaddfunction's return type asany. Disabling this option can lead toanys throughout your codebase, with no indication that they're there!Option 2:
strictNullChecks. This is probably the most important one! With this option enabled, TypeScript doesn't allow us to assignnullorundefinedto variables unless their types includenullorundefined.>
/* Without the `strictNullChecks` compiler option, this variableassignment would be allowed! */const n: number = null;n + 1;Result:
type error: Type 'null' is not assignable to type 'number'.
>
/* Without the `strictNullChecks` compiler option, this variableassignment would be allowed! */const s: string = undefined;'Hello, ' + s;Result:
type error: Type 'undefined' is not assignable to type 'string'.
>
const n: number = undefined;10 / n;Result:
type error: Type 'undefined' is not assignable to type 'number'.
With
strictNullChecksenabled, those are all type errors. But if we disable that option, we get1,'Hello, undefined', andNaN, respectively. None of those make sense, and all three are almost certainly bugs.If you've ever seen the words
undefined,null, orNaNshow up in a web page, there's a good chance that this kind of mistake caused that bug. Accordingly, if the team had used TypeScript withstrictNullChecks, that bug might have been caught at compile time!We strongly encourage you to always use
strictNullChecks, even if you decide to disable some of the other strict options. This option allows you to take advantage of one of the most helpful parts of TypeScript: preventing unexpectednulls andundefineds from propagating through your system at runtime.Many statically typed languages allow us to make this type of mistake with
null, but have no equivalent of thestrictNullChecksoption. For example, C++, Java, and Go all allow this mistake. (Go has "nil" instead of "null", but that's a different name for the same idea.)Fortunately, newer statically typed languages tend to be strict about null, including Elm, Reason, Kotlin, Swift, and many others. Nulls are always strict in those languages, with no need to enable an option like TypeScript's
strictNullChecks.Option 3:
strictPropertyInitialization. In the code below, our class has anageproperty, but we never initialize it. Because ofstrictPropertyInitialization, that's a type error.>
class User {name: string;age: number;constructor(name: string) {this.name = name;/* We don't assign `this.age` here. Without the* `strictPropertyInitialization` compiler option enabled, this would* be allowed! */}}const amir: User = new User('Amir');amir.age;Result:
type error: Property 'age' has no initializer and is not definitely assigned in the constructor.
Without
strictPropertyInitialization, this code would type check and returnundefined, even though the type ofageis declared asnumber.Option 4:
strictFunctionTypes. The rules for function type compatibility are more complex than for other types. We cover that in detail in a later lesson. For now, we'll just say that this option prevents some mistakes with functions. Those mistakes are very subtle, and can lead to very difficult bugs.These options should give you a good sense for the value of enabling strict mode. However, there are even more strictness options enabled by
"strict": true. For example,alwaysStrict,noImplicitThis, andstrictBindCallApply. You can find a full list in the compiler options documentation.It's important to remember that strict mode is disabled by default. If you don't enable strict mode, all of the mistakes shown above are allowed; none of them cause type errors.
We recommend always setting
{"compilerOptions": {"strict": true}}in your "tsconfig.json" file. Then you won't have to worry about any of these problems. The compiler will worry about them for you, which is the reason that we use TypeScript in the first place!