TypeScript: Putting tsconfig on the shelves. Part 2 - Everything about strictness

In the last article, I covered the various features of some common TypeScript settings. This article will focus on the so-called "strict flags".





In fact, out of the box TypeScript is not much different from JavaScript. Therefore, if the project config is not initially tweaked, then most of the advantages of the language will not be used. There is a sense of using TypeScript in this form, but not much.





, : tsconfig.json



, strict



compilerOptions



true



. TypeScript . , ยซยป , . .





tsconfig.json



. : Strict Checks



Linter Checks



โ€“ . Advanced



.





Strict Checks

, . : strict



, alwaysStrict



, noImplicitAny



, strictNullChecks



, strictFunctionTypes



, strictPropertyInitialization



, noImplicitThis



, strictBindCallApply



.





false



, , โ€“ true



.





, , , , - strict



alwaysStrict



. .





alwaysStrict

: / : .





alwaysStrict



"use strict"



. , alwaysStrict



JavaScript TypeScript.





strict

: / : / .





strict



. Strict Checks



, alwaysStrict



. , .





โ€“ . strict: true



, , . , TypeScript , , JavaScript.





. . , - false



.





strict



โ€“ TypeScript. , release notes , , .





:





{
  "compilerOptions": {
    "alwaysStrict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "strictFunctionTypes": true,
    "noImplicitThis": true,
    "strictBindCallApply": true,
  }
}
      
      



, , .





noImplicitAny

: / :





, TypeScript.





any



. , , . JavaScript. TypeScript, JavaScript any



, .





any



ยซ , ยป. , . TypeScript :





//   
let a: number = 5
//  
let b = 'hello'

//        
// value     any
function someFunction (value) {
  //       
  console.log(value.subtr(3))
}
      
      



, , any



, JavaScript, TypeScript . . . , TypeScript JavaScript . noImplicitAny



, .





, any



. , (implicit) any



, - .





//         any
function someFunction (value: any) {
  console.log(value.subtr(3))
}
      
      



any



, . noImplicitAny



.





ESLint no-explicit-any



. any



warning, .





strictNullChecks

: / :





TypeScript. noImplicitAny



.





JavaScript โ€“ undefined



null



, TypeScript . , . : string



, boolean



, number



. โ€“ undefined



null



:





function someFunction (value: number) {
  //      value  undefined  null
  return value * 2
}

someFunction(5)
//    
someFunction(null)
//   
someFunction(undefined)
      
      



( ) . null



(undefined



). . , Java NullPointerException .





strictNullChecks



. undefined



null



, , . :





function someFunction (value: number) {
  // value    number
  return value * 2
}

someFunction(5)
//    
someFunction(null)
someFunction(undefined)
      
      



undefined



null



, . , , . , .





//  ยซ?ยป  undefined,  ยซ| nullยป - null
function someFunction (value?: number | null) {
  if (value == null) {
    return 0
  }
  return value * 2
}
      
      



. . (, ), , . , , null



, . . , json- .





. , , JavaScript . .





strictNullChecks



โ€“ Any



, unknown



, object



, void



, undefined



, null



and never



assignability.





strictPropertyInitialization

: / : / strictNullChecks







strictPropertyInitialization



, :





class User {
  name: string
  // email    ,   
  //  ,    
  email: string

  constructor (name: string) {
    this.name = name
  }
}
      
      



strictNullChecks



.





strictFunctionTypes

: / :





strictFunctionTypes: true



. , :





interface StringOrNumberFunc {
  (value: string | number): void
}

function someFunction (value: string) {
  console.log(value)
}

//   
// string | number   string
let func: StringOrNumberFunc = someFunction

func(10)
func('10')
      
      



noImplicitThis

: / :





this



, . :





class SomeClass {
  multiplier: number = 5

  createSomeFunction (value: number) {
    return function () {
      //   -  this     SomeClass
      return value * this.multiplier
    }
  }
}
      
      



this



, function



. , function



arrow function



.





, - this



:





function sayHello (name: string) {
  console.log(this.helloWord + ' ' + name)
}
      
      



this



. this



bind



, call



, apply



. :





//   this  ยซยป
//       
function sayHello (this: HelloStyle, name: string) {
  console.log(this.helloWord + ' ' + name)
}

//   
interface HelloStyle {
  helloWord: string
}

class HawaiiStyle implements HelloStyle {
  helloWord = 'Aloha'
}

class RussianStyle implements HelloStyle {
  helloWord = ','
}

//  
sayHello.bind(new HawaiiStyle())('World')
sayHello.call(new RussianStyle(), 'World')
sayHello.apply(new RussianStyle(), ['World'])
      
      



.





strictBindCallApply

: / :





strictBindCallApply



โ€“ : bind



, call



, apply



.





function someFunction (value: string) {
  console.log(value)
}

someFunction.call(undefined, '10')
// ,      
someFunction.call(undefined, false)
      
      



. // @ts-ignore



.





Strict Checks

  • . TypeScript





  • strict: true



    . , TypeScript.





  • alwaysStrict



    TypeScript, JavaScript. . . "use strict"



    .





  • noImplicitAny



    strictNullChecks



    strictPropertyInitialization



    . strictFunctionTypes



    noImplicitThis



    . strictBindCallApply



    .





Linter Checks

โ€“ , , . ESLint. Linter Checks



ESLint. :





{
  "compilerOptions": {
    "noPropertyAccessFromIndexSignature": true,
    "noUncheckedIndexedAccess": true,
    
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
  }
}
      
      



false



, , โ€“ true



.





noPropertyAccessFromIndexSignature



noUncheckedIndexedAccess



. , , Strict Checks



. , strict



.





, . noPropertyAccessFromIndexSignature



noUncheckedIndexedAccess



. , Strict Checks



, strict



.





ESLint. , ESLint TSLint. TSLint Migration Guide ESLint rules for TSLint.





.





noPropertyAccessFromIndexSignature

: / : / /





noPropertyAccessFromIndexSignature



aka dot notation



, , (aka arbitrarily-named properties, index signatures).





interface User {
  //   
  login: string
  email: string

  //  
  [key: string]: string
}

const user: User = {
  login: 'hello',
  email: 'hello@example.com'
}

// c noPropertyAccessFromIndexSignature: true
//     
const username = user.name

//      
const username2 = user['name']
      
      



aka bracket notation. noImplicitAny



- .





โ€“ . , . dot notation



, , . . .





, noUncheckedIndexedAccess



.





noUncheckedIndexedAccess

: / : / / strictNullChecks



/





.





interface User {
  //   
  login: string
  email: string

  //  
  [key: string]: string
}

const user: User = {
  login: 'hello',
  email: 'hello@example.com'
}

//   username - string
const username = user['name']
      
      



, username



string.



, . : [key: string]: string | undefined



. , . , - undefined



.





noUncheckedIndexedAccess



. .





. :





const strings: string[] = ['hello']
//   number    string
const number = strings[100]
      
      



, undefined



. noUncheckedIndexedAccess



string | undefined



.





, ยซยป , undefined



โ€“ . ?





:





const strings: string[] = ['hello']
//     number     string | undefined
const number = strings[0]
      
      



:





function upperCaseAll(strings: string[]) {
  for (let i = 0; i < strings.length; i++) {
      //    
      console.log(strings[i].toUpperCase());
  }
}
      
      



, , , . .





noUncheckedIndexedAccess



, TypeScript. , .





strictNullChecks



.





noImplicitReturns

: / : / ESLint: consistent-return,





, :





function lookupHeadphonesManufacturer(color: 'blue' | 'black'): string {
  if (color === 'blue') {
    return 'beats'
  }
  //  return
}
      
      



ESLint consistent-return



, TypeScript.





noFallthroughCasesInSwitch

: ESLint / : / ESLint: no-fallthrough





break



switch/case



:





switch (value) {
  case 0:
    console.log('even')
    //  break
  case 1:
    console.log('odd')
    break
}
      
      



no-fallthrough



.





noUnusedLocals

: production ESLint / : / ESLint: no-unused-vars





:





function createKeyboard (modelID: number) {
  //   
  const defaultModelID = 23

  return {
    type: 'keyboard',
    modelID
  }
}
      
      



. ยซยป , .





development



ESLint no-unused-vars



.





noUnusedParameters

: production ESLint / : / ESLint: no-unused-vars





:





function createDefaultKeyboard (modelID: number) {
  const defaultModelID = 23

  return {
    type: 'keyboard',
    modelID: defaultModelID
  }
}
      
      



noUnusedParameters



. development



ESLint no-unused-vars



.





Linter Checks







  • ,





  • noPropertyAccessFromIndexSignature



    noUncheckedIndexedAccess



    Strict Checks



    , strict







  • : noFallthroughCasesInSwitch



    , noUnusedLocals



    , noUnusedParameters



    . noImplicitReturns







Advanced

, , . . :





{
  "compilerOptions": {
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    
    "noImplicitUseStrict": false,
    "suppressExcessPropertyErrors": false,
    "suppressImplicitAnyIndexErrors": false,
    "noStrictGenericChecks": false,
  }
}
      
      



allowUnreachableCode



allowUnusedLabels



โ€“ Linter Checks



.





true



, false



โ€“ .





. no



, . . ยซ ยป. allow



โ€“ ยซ ยป. - noUnreachableCode



noUnusedLabels



.





: noImplicitUseStrict



, noStrictGenericChecks



, suppressExcessPropertyErrors



suppressImplicitAnyIndexErrors



Base Strict Checks.



:





false



false



!





, TypeScript !





, ( noImplicitUseStrict



) , JavaScript.





99.9% .





- .





allowUnreachableCode

: production / : / ESLint: no-unreachable,





โ€“ , return, throw, break, continue:





function fn (n: number) {
  if (n > 5) {
    return true
  } else {
    return false
  }

  //  
  return true
}
      
      



development



. no-unreachable



, TypeScript.





allowUnusedLabels

: production ESLint / : / ESLint: no-unused-labels





. , , :





function verifyAge (age: number) {
  if (age > 18) {
    //    label
    verified: true
  }
}
      
      



no-unused-labels



.





noImplicitUseStrict

, / alwaysStrict







"use strict"



target



, ES6



. alwaysStrict



, target



. - .





noImplicitUseStrict



alwaysStrict



true



, , .





suppressExcessPropertyErrors

,





, , :





interface Point {
  x: number
  y: number
}

const p: Point = {
  x: 1,
  y: 3,
  //  z     Point
  z: 10
}
      
      



JavaScript, . . // @ts-ignore



.





suppressImplicitAnyIndexErrors

, / noImplicitAny







, , , . noUncheckedIndexedAccess



:





interface User {
  //   
  login: string
  email: string

  //   
  // [key: string]: string
}

const user: User = {
  login: 'hello',
  email: 'hello@example.com'
}

//    name 
const username = user['name']
      
      



, . , - . , , , TypeScript . declaration merging



.





. , Google Maps, script. Pin:





const pin = new window.google.maps.Pin(59.9386, 30.3141)
      
      



, , google. // @ts-ignore



, . โ€“ suppressImplicitAnyIndexErrors: true



( ) :





const pin = new window['google']['maps']['Pin'](59.9386, 30.3141)
      
      



. . :





//   merging.d.ts
interface Pin {
  // 
}

interface PinConstructor {
  new(lat: number, lng: number): Pin
}

interface Window {
  google: {
    maps: {
      Pin: PinConstructor
    }
  }
}

//   
const pin = new window.google.maps.Pin(59.9386, 30.3141)
      
      



, req



res



Express.





noStrictGenericChecks

,





ยซ ยป generics



:





type A = <T, U>(x: T, y: U) => [T, U]
type B = <S>(x: S, y: S) => [S, S]

function f (a: A, b: B) {
  // OK
  b = a
  //   ,   
  a = b
}
      
      



Advanced







  • . .





  • allowUnreachableCode



    allowUnusedLabels



    , Linter Checks







  • noImplicitUseStrict



    , noStrictGenericChecks



    , suppressExcessPropertyErrors



    suppressImplicitAnyIndexErrors



    , TypeScript .









  • noImplicitUseStrict



    alwaysStrict



    .





  • noStrictGenericChecks



    , suppressExcessPropertyErrors



    suppressImplicitAnyIndexErrors



    , JavaScript TypeScript.





, :





tsconfig-checks.json



:





{
  "compilerOptions": {
    // Strict Checks
    "alwaysStrict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "strictFunctionTypes": true,
    "noImplicitThis": true,
    "strictBindCallApply": true,
    "noPropertyAccessFromIndexSignature": true,
    "noUncheckedIndexedAccess": true,
    // Linter Checks
    "noImplicitReturns": true, // https://eslint.org/docs/rules/consistent-return ?
    "noFallthroughCasesInSwitch": true, // https://eslint.org/docs/rules/no-fallthrough
    "noUnusedLocals": true, // https://eslint.org/docs/rules/no-unused-vars
    "noUnusedParameters": true, // https://eslint.org/docs/rules/no-unused-vars#args
    "allowUnreachableCode": false, // https://eslint.org/docs/rules/no-unreachable ?
    "allowUnusedLabels": false, // https://eslint.org/docs/rules/no-unused-labels
    // Base Strict Checks
    "noImplicitUseStrict": false,
    "suppressExcessPropertyErrors": false,
    "suppressImplicitAnyIndexErrors": false,
    "noStrictGenericChecks": false,
  }
}
      
      



tsconfig-checks.json



? , , , . .





tsconfig.json



:





{
  //  
  "extends": "./tsconfig-checks.json",
  "compilerOptions": {
    //   
  }
}
      
      



And for the convenience of development, so that unattainable code and unused variables do not break compilation for us, we can do the following.





File tsconfig-dev.json



:





{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    //   ,    
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "allowUnreachableCode": true,
    "allowUnusedLabels": true
  }
}
      
      



That's all. In the future, I plan to figure out the optimization and performance options and be sure to share with you. Until next time!





The article is based on my thread in the @jsunderhood collective account .





PS: In my @barinbritva account, I also sometimes write about TypeScript and development.








All Articles