--strict flag includes the following flags:
--strictNullChecks--alwaysStrict--noImplicitAny--noImplicitThis--strictBindCallApply--strictFunctionTypes--strictPropertyInitializationWe give examples and try to understand in one place what all this means.// I. --strictNullChecks
The famous problem with NPE (null pointer exception, billion dollar mistake) in the context of TS.By default, in TS all types are Nullable and this means that we can pass “undefined” | “Null” where any other type is expected (even a primitive):const bar1: { foo: number } = undefined;
const bar2: { foo: number } = null;
const bar3: number = null;
const bar4: string = null;
More interesting examples are a method call, which may not bedeclare var smth: { optionalMethod?(): string; };
smth.optionalMethod();
It is also implied that we cannot return “undefined” | “Null” where this is clearly not expectedfunction getIt(): { data: number } {
return undefined;
}
getIt().data;
We’ll have to explicitly indicate that “undefined” can return and only after that we get an errorfunction getIt(): { data: number } | undefined {
return undefined;
}
getIt().data;
And as a bonus - safer operations, where there may be no result, there will be an error with the flag turned on and you will have to explicitly check that “find” found something:
[{ name: 'John', age: 4 }]
.find(el => el.age === 42)
.name;
// II. --alwaysStrict
Adds 'use strict' annotation to each file, making JS behavior more explicit// III. --noImplicitAny
Prohibits the implicit use of 'any' in TS, i.e. code without type annotation
function id(arg) {
return arg;
}
Great help with untyped imports from third-party libraries suggesting to install type definitions
import * as session from '3rd-party-lib';
// IV. --strictBindCallApply
It includes a “more stringent” type check for “bind” / ”call” / ”apply”, without a flag - this is all a valid TS. function getFullName(name: string, surname: string): string {
return name + surname;
}
getFullName.call(null, 'John', 42);
getFullName.apply(null, ['John', 42]);
getFullName.bind(null)('John');
getFullName.bind(null, 'John')();
getFullName.bind(null, 'John')(42);
// V. --strictPropertyInitialization + --strictNullChecks
Helps track that all properties have been initialized in the constructor; you must also enable --strictNullChecks to disable Nullable types. class User {
name: string;
}
However, if the assignment is not in the constructor itself, convince TS that everything is ok. class User2 {
name: string;
constructor(name: string) {
this.initializeName();
}
initializeName() {
this.name = 'John'
}
}
If you couldn’t convince TS that the property will be exactly initialized, you can say “I swear by Mom, I will definitely initialize!” or more briefly “!”class User3 {
name!: string;
}
// VI. --strictFunctionTypes
Removes a bivariant check for arguments.Variant in programming, in short - this is the ability to pass Supertype / Subtype there, where Type is expected. For example, there is a hierarchy Shape -> Circle -> Rectangle, is it possible to transfer or return a Shape / Rectangle if Circle is expected ?Programming Option habr , SOinterface Shape { name: string };
interface Circle extends Shape { width: number };
interface Rectangle extends Circle { height: number };
declare var logSC: (figure: Shape) => Circle;
declare var logRC: (figure: Rectangle) => Circle;
declare var logCC: (figure: Circle) => Circle;
declare var logCS: (figure: Circle) => Shape;
declare var logCR: (figure: Circle) => Rectangle;
declare var wlogBB: (fn: (figure: Circle) => Circle) => void;
wlogBB(logCC);
wlogBB(logSC);
wlogBB(logCR);
wlogBB(logCS);
wlogBB(logRC);
It is understood that the function should not mutate the passed argument (acting as type producer), there are no errors in TS, in fact - there areconst squares: Square[] = [{ name: 'Square', width: 5 }];
function addSmth(arg: Shape[]) {
arg.push({ name: 'Square' });
}
addSmth(squares);
// VII. --noImplicitThis
If the function is defined outside the object / class, then TS will ask you to explicitly indicate what “this” will refer to using the first pseudo-argument named “this”
function getName(this: { name: string }, surname: string): string {
return this.name;
}
getName.call({}, 'Smith');
getName.apply({}, ['Smith']);
getName.bind({})('Smith');
Calls will be validconst somePerson = { name: 'John', getName };
const fullName: string = somePerson.getName('Smith')
getName.call({name: 'John'}, 'Smith');
getName.apply({name: 'John'}, ['Smith']);
getName.bind({name: 'John'})('Smith');
Constructor functions can cause problemsfunction Person(this: { name: string }, name: string) {
this.name = name;
}
const person = new Person('John');
An interesting bonus is adding a comparison of context binding methods for classes.class A {
x = 42;
constructor() {
this.getBound = this.getBound.bind(this);
}
getSimple(): number {
return this.x;
}
getSimpleAnnotated(this: A): number {
return this.x;
}
getArrow = (): number => this.x;
getBound(this: A): number {
return this.x;
}
}
const a = new A();
const getSimple = a.getSimple;
getSimple();
const getSimpleAnnotated = a.getSimpleAnnotated;
getSimpleAnnotated();
const getArrow = a.getArrow;
getArrow();
const getBound = a.getBound;
getBound();