How strict is Typescript’s strict mode?

How strict is Typescript’s strict mode?BriwaBlockedUnblockFollowFollowingJun 1Even more strict than the regular strict.

Photo by by Mark Duffel on Unsplash.

I started out writing code in Javascript without proper typing.

When I switched to Typescript, I migrated my code without turning the strict mode on because I knew that the typing refactor was going to be too much to handle, and I should focus on passing the unit tests first.

Even without the strict mode, it was already a shift of paradigm because you have to specifically define most of the things unlike Javascript.

I thought I’ve already strict enough with my types back then.

But how strict is strict mode?According to the docs, when Typescript strict mode is set to on, it will validate your code using the strict type rules under the ‘strict’ family to all files in the project.

The rules are:noImplicitAnynoImplicitThisstrictNullChecksstrictPropertyInitializationstrictBindCallApplystrictFunctionTypesThese are some of the lessons I learned when I bumped into these rules.

1.

noImplicitAnyThis rule disallows variables or function arguments to have an implicit any type.

Consider this example:// Javascript/Typescript non-strict modefunction extractIds (list) { return list.

map(member => member.

id)}Looking at the code, list can be anything.

Sure, from the .

map you would think that it is an array of members, and the member has property called id but there isn't anything that specifically defines that.

This is why it is an error in strict mode.

// Typescript strict modefunction extractIds (list) { // ❌ ^^^^ // Parameter 'list' implicitly // has an 'any' type.

ts(7006) return list.

map(member => member.

id) // ❌ ^^^^^^ // Parameter 'member' implicitly // has an 'any' type.

ts(7006)}A fixed version would be:// Typescript strict modeinterface Member { id: number name: string}function extractIds (list: Member[]) { return list.

map(member => member.

id)}Another common code that you might see in the wild:// Typescript strict modefunction onChangeCheckbox (e) { // ❌ ^ // Parameter 'e' implicitly // has an 'any' type.

ts(7006) e.

preventDefault() const value = e.

target.

checked validateCheckbox(value)}You can use some of the global types defined by the Typescript itself for, in this case, browser-specific types:// Typescript strict modeinterface ChangeCheckboxEvent extends MouseEvent { target: HTMLInputElement}function onChangeCheckbox (e: ChangeCheckboxEvent) { e.

preventDefault() const value = e.

target.

checked validateCheckbox(value)}Do note that it would also throw an error if you import libraries that has no type definitions, because that would imply that the imported library has an any type.

// Typescript strict modeimport { Vector } from 'sylvester'// ❌ ^^^^^^^^^^^// Could not find a declaration file // for module 'sylvester'.

// 'sylvester' implicitly has an 'any' type.

// Try `npm install @types/sylvester` // if it exists or add a new declaration (.

d.

ts)// file containing `declare module 'sylvester';`// ts(7016)It can be a bottleneck in your project since you might end up having to write that type definitions yourself if there isn’t any, but having all types defined should’ve been the right thing to do anyway, at least in strict mode.

2.

noImplicitThisThis rule disallows this context to be implicitly defined.

Consider this example:// Javascript/Typescript non-strict modefunction uppercaseLabel () { return this.

label.

toUpperCase()}const config = { label: 'foo-config', uppercaseLabel}config.

uppercaseLabel()// FOO-CONFIGFor someone who’s been writing Javascript, it is known that this refers to the config object, so this.

label would just be retrieving config.

label, which is why this code works.

However, referring to this on a function can be ambiguous.

// Typescript strict modefunction uppercaseLabel () { return this.

label.

toUpperCase() // ❌ ^^^^ // 'this' implicitly has type 'any' // because it does not have a type annotation.

ts(2683)}If we run uppercaseLabel alone, it would throw an error because this context is not on config anymore, hence the error because label is undefined.

One way to fix it is to avoid using this on a function without a context:// Typescript strict modeconst config = { label: 'foo-config', uppercaseLabel () { return this.

label.

toUpperCase() }}Typescript won’t even complain on this because all types are inferred properly.

Or, even better, write the interface, so that all types are now defined instead of inferred.

// Typescript strict modeinterface MyConfig { label: string uppercaseLabel: (params: void) => string}const config: MyConfig = { label: 'foo-config', uppercaseLabel () { return this.

label.

toUpperCase() }}3.

strictNullChecksThis rule validates the possibility of values returning null or undefined.

Consider this example:// Javascript/Typescript non-strict modefunction getArticleMetaById (articles: Article[], id: string) { const article = articles.

find(article => article.

id === id) return article.

meta}Now, of course I would have checked the code first in the browser if it works (and it did).

However, in strict mode, Typescript would remind me that there are chances that .

find would return undefined when none of the ids in the responses would match the given id.

// Typescript strict modefunction getArticleMetaById (articles: Article[], id: string) { const article = articles.

find(article => article.

id === id) return article.

meta // ❌ ^^^^^^^ // Object is possibly 'undefined'.

ts(2532)}This would actually broaden my code specification so that now I have to actually handle error cases as well, which should’ve been done in the first place.

// Typescript strict modefunction getArticleMetaById (articles: Article[], id: string) { const article = articles.

find(article => article.

id === id) if (typeof article === 'undefined') { throw new Error(`Could not find an article with id: ${id}.

`) } return article.

meta}4.

strictPropertyInitializationThis rule validates properties in a class to be initialized either inside a constructor function or already defined before constructed.

Consider this example:// Javascriptclass Student { constructor (grade, lessons) { this.

grade = grade this.

lessons = lessons.

filter(lesson => lesson.

grade <= grade) } setRedoLessons (lessons) { this.

redoLessons = lessons }}With Typescript, all class instance properties can be defined properly.

// Typescript non-strict modeinterface Lesson { title: string grade: number}class Student { private grade: number private lessons: Lesson[] private redoLessons: Lesson[] private greetingType: string constructor (grade: number, lessons: Lesson[]) { this.

grade = grade this.

lessons = lessons.

filter(lesson => lesson.

grade <= grade) } setRedoLessons (lessons: Lesson[]) { this.

redoLessons = lessons }}However, at this point you couldn’t tell whether there is a property that isn’t defined either in the constructor function or in some method.

I don’t know if you noticed but in the previous code I sneaked in a property that meets such criteria.

// Typescript non-strict modeinterface Lesson { title: string grade: number}class Student { private grade: number private lessons: Lesson[] private redoLessons: Lesson[] private greetingType: string // ????.This is undefined, // not used and there's no error!.constructor (grade: number, lessons: Lesson[]) { this.

grade = grade this.

lessons = lessons.

filter(lesson => lesson.

grade <= grade) } setRedoLessons (lessons: Lesson[]) { this.

redoLessons = lessons }}In strict mode, it actually throws errors on all undefined properties not defined in the constructor.

// Typescript strict modeinterface Lesson { title: string grade: number}class Student { private grade: number private lessons: Lesson[] private redoLessons: Lesson[] // ❌ ^^^^^^^^^^^ // Property 'redoLessons' has no initializer and // is not definitely assigned in the constructor.

ts(2564) private greetingType: string // ❌ ^^^^^^^^^^^^ // Property 'greetingType' has no initializer and // is not definitely assigned in the constructor.

ts(2564) constructor (grade: number, lessons: Lesson[]) { this.

grade = grade this.

lessons = lessons.

filter(lesson => lesson.

grade <= grade) } setRedoLessons (lessons: Lesson[]) { this.

redoLessons = lessons }}This helps you review the code and see whether the properties are indeed being used in places other than the constructor.

If it does, you can put an !.on it and simply remove those who aren't.

// Typescript strict modeinterface Lesson { title: string grade: number}class Student { private grade: number private lessons: Lesson[] private redoLessons!: Lesson[] constructor (grade: number, lessons: Lesson[]) { this.

grade = grade this.

lessons = lessons.

filter(lesson => lesson.

grade <= grade) } setRedoLessons (lessons: Lesson[]) { this.

redoLessons = lessons }}However, I would recommend to either set it to a default value if it’s not defined in the constructor as a good practice, otherwise it would be forever undefined until it is set (unless that is intentional).

5.

strictBindCallApplyThis rule validates the usage of bind, call or apply as defined in the function.

Consider this example:// Typescript without strict modefunction sum (num1: number, num2: number) { return num1 + num2}sum.

apply(null, [1, 2])// 3Maybe a mistake is made, thinking that the sum can take in more than two arguments.

When the code is run, there is no error thrown on Typescript and in your environment (browser, perhaps).

// Typescript non-strict modefunction sum (num1: number, num2: number) { return num1 + num2}sum.

apply(null, [1, 2, 3])// Still 3.

?The only way to know if it’s a mistake is when the code is tested manually or in a unit test.

In strict mode, you can spot this even before that:// Typescript strict modefunction sum (num1: number, num2: number) { return num1 + num2}sum.

apply(null, [1, 2, 3])// ❌ ^^^^^^^^^// Argument of type '[number, number, number]' is not // assignable to parameter of type '[number, number]'.

// Types of property 'length' are incompatible.

// Type '3' is not assignable to type '2'.

ts(2345)Then it might be a good time to rethink of the sum function design.

// Typescript strict modefunction sum (.

args: number[]) { return args.

reduce<number>((total, num) => total + num, 0)}sum.

apply(null, [1, 2, 3])// 66.

strictFunctionTypesUnfortunately, I have yet to find the use case of this rules in my code so far, so I can’t comment much on it.

You can always check out the release notes for strictFunctionTypes on the docs.

If anyone has a use case to share, let me know!If you want to take Typescript restrictions to a different level, I recommend using tslint, though I would say some of the rules are based on preferences, but there are a lot of useful ones.

Or, avoid bikeshedding by choosing a standard such as gslint or tslint-config-standard.

I hope you find this article useful!.Thanks for reading.

Originally published at https://dev.

to on June 1, 2019.

.

. More details

Leave a Reply