TypeScript: Mettre tsconfig sur les étagères. Partie 2 - Tout sur la rigueur

Dans le dernier article, j'ai couvert les différentes fonctionnalités de certains paramètres TypeScript courants. Cet article se concentrera sur les soi-disant «indicateurs stricts».





En fait, TypeScript prêt à l'emploi n'est pas très différent de JavaScript. Par conséquent, si vous ne modifiez pas initialement la configuration du projet, la plupart des avantages du langage ne seront pas utilisés. Il y a un sens à utiliser TypeScript sous cette forme, mais pas beaucoup.





, : 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": {
    //   
  }
}
      
      



Et pour la commodité du développement, afin que le code inaccessible et les variables inutilisées ne rompent pas la compilation pour nous, nous pouvons faire ce qui suit.





Fichier tsconfig-dev.json



:





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



C'est tout. À l'avenir, je prévois de déterminer les options d'optimisation et de performance et de partager avec vous. Jusqu'à la prochaine fois!





L'article est basé sur mon fil de discussion dans le compte collectif @jsunderhood .





PS: Dans mon compte @barinbritva , j'écris aussi parfois sur TypeScript et le développement.








All Articles