TypeScript: poner tsconfig en los estantes. Parte 2 - Todo sobre el rigor

En el último artículo, cubrí las diversas características de algunas configuraciones comunes de TypeScript. Este artículo se centrará en las llamadas "banderas estrictas".





De hecho, TypeScript listo para usar no es muy diferente de JavaScript. Por lo tanto, si no modifica inicialmente la configuración del proyecto, no se utilizarán la mayoría de las ventajas del lenguaje. Existe la sensación de usar TypeScript en esta forma, pero no mucho.





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



Y para la conveniencia del desarrollo, para que el código inalcanzable y las variables no utilizadas no interrumpan la compilación para nosotros, podemos hacer lo siguiente.





Archivo tsconfig-dev.json



:





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



Eso es todo. En el futuro, planeo descubrir las opciones de optimización y rendimiento y asegurarme de compartirlas con ustedes. ¡Hasta la proxima vez!





El artículo está basado en mi hilo en la cuenta colectiva @jsunderhood .





PD: En mi cuenta @barinbritva , a veces también escribo sobre TypeScript y desarrollo.








All Articles