๊ฒฝ์ฐฐ 24์‹œ๐Ÿ‘ฎโ€โ™‚๏ธ

11105 ๋‹จ์–ด TypeScript
์ด๊ฒƒ์€ TypeScript Advent Calendar 2019 22์ผ์งธ ๋Œ€ํƒ€ ๋ณด๋„๋‹ค.
๋งˆ์ง€๋ง‰ ๊ฒŒ์‹œ๋ฌผ CLI ๋„๊ตฌํ™”, any ๊ฒฝ์ฐฐ๐Ÿ‘ฎโ€โ™‚๏ธ๋””๋ฒ„๊น…์„ ์ง„ํ–‰ํ•˜๋‹ค.์ƒ๊ธฐ ํŒŒ์ผ์—์„œ ์„ค๋ช…ํ•œ ๋ฐ”์™€ ๊ฐ™์ด devDependencies์— ์ถ”๊ฐ€ํ•˜๊ณ  ์‹คํ–‰npx anycopํ•˜๋ฉด ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•ดany ์ง„๋‹จ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
anycop: https://www.npmjs.com/package/anycop

์‚ฌ์ ˆํ•˜๋‹ค


์ด๊ฒƒ์€ ์• ๋‹ˆ๋ฅผ ์ซ“์•„๋‚ด๊ธฐ ์œ„ํ•ด ๋งŒ๋“  ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.any์˜ ์‚ฌ์šฉ ์ƒํ™ฉ์ด ๊ฐ€์‹œํ™”๋˜๋ฉด ๊ณผ์ œ์ ์„ ํŒŒ์•…ํ•˜๊ณ  ํŒ€์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.์‹œ๊ฐ„์ด ์žˆ์„ ๋•Œ ํ–ฅ์ƒํ˜• ์•ˆ์ „ ์ปค๋ฒ„ ์กฐ์น˜๋ฅผ ์ทจํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ •๋ง ์ข‹์€ ์ผ์ด๋‹ค.any ๋‚˜์œ ์‚ฌ๋žŒ ์•„๋‹ˆ์—์š”.TypeScript์— ์ด ์‚ฌ์–‘์ด ์—†์œผ๋ฉด ํ˜„์žฌ JavaScript ์ƒํƒœ๊ณ„์—์„œ ์ •์  ์œ ํ˜•์„ ์ ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.์ด ๋„๊ตฌ๋กœany์™€ ๋” ์ข‹์€ ๊ด€๊ณ„๋ฅผ ๋งบ์„ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ข‹๊ฒ ๋‹ค.

๋ณ€๊ฒฝ ์‚ฌํ•ญ


์ง€๋‚œ๋ฒˆ ํˆฌ๊ณ ๋Š”'๋ณ€์ˆ˜ ์ •์˜์˜any์ถ”๋ฆฌ'์— ํ•œ์ •๋˜์—ˆ์ง€๋งŒ ํž˜์„ ๋†’์—ฌ ๋‹ค์Œany๋ฅผ ๊ฒ€์ถœํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.
  • ๋ณ€์ˆ˜ ์„ฑ๋ช…์˜anyํ˜• ์ถ”๋ฆฌ
  • ํ•จ์ˆ˜ ๋งค๊ฐœ ๋ณ€์ˆ˜์˜anyํ˜• ์ถ”๋ฆฌ
  • Binding Element์˜anyํ˜• ์ถ”๋ฆฌ
  • ํ•จ์ˆ˜์˜any๋ฐ˜ํ™˜ํ˜• ์ถ”๋ฆฌ
  • Arrow ํ•จ์ˆ˜์˜any ๋ฐ˜ํ™˜ํ˜• ์ถ”๋ฆฌ
  • AsExpression(as any ๋‹จ์–ธ)
  • TypeScript ํ”„๋กœ์ ํŠธ์— ์„ค์น˜ํ•˜๊ณ  ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

    ๋ณ€์ˆ˜ ์„ฑ๋ช…์˜anyํ˜• ์ถ”๋ฆฌ


    ๊ทธ๊ฒƒ์€ ๋ณ€์ˆ˜์˜ ํ˜„์‹any ์ฃผ์„์„ ๊ฒ€์ถœํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผany ์ถ”๋ฆฌ๋„ ๊ฒ€์ถœํ•œ๋‹ค.
    function greet(message: any) {
      return message
    }
    // inferred "any" at VariableDeclaration
    const message = greet('hello')
    

    ํ•จ์ˆ˜ ๋งค๊ฐœ ๋ณ€์ˆ˜์˜anyํ˜• ์ถ”๋ฆฌ


    ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ํ˜„์‹any ์ฃผ์„์„ ๊ฒ€์ถœํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์ธ์šฉ์˜ ์œ ํ˜• ์ •์˜๊ฐ€any์— ํ•ด๋‹นํ•˜๋Š” ์ƒํ™ฉ๋„ ๊ฒ€์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
    // annotation of "any" at ParameterDeclaration (message)
    function greet(message: any) {
      return message
    }
    

    Binding Element์˜ any ์ถ”๋ฆฌ


    ๊ท€์† ์š”์†Œ์—์„œ ์ „๊ฐœ๋˜๋Š” ์ธ์šฉ์ดany ์ถ”๋ฆฌ์ผ ๋•Œ๋„ ๊ฒ€์ถœ๋ฉ๋‹ˆ๋‹ค.
    type Props = { a: any, b: any }
    // inferred "any" at BindingElement (a & b)
    function greet({ a, b }: Props) {
      return { a, b }
    }
    

    ํ•จ์ˆ˜์˜any ๋ฐ˜ํ™˜ ์ถ”๋ฆฌ


    ๋˜๋Œ์•„์˜ค๋Š” ํ˜•์‹์˜ ํ˜„์‹any ์ฃผ์„์„ ๊ฒ€์ถœํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผany์˜ ๋˜๋Œ์•„์˜ค๋Š” ํ˜•์‹๋„ ๊ฒ€์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
    // inferred "any" at FunctionDeclReturn
    function greet() {
      const message: any = 'hello'
      return message
    }
    

    arrow ํ•จ์ˆ˜์˜any ๋ฐ˜ํ™˜ ์ถ”๋ฆฌ


    ๋˜๋Œ์•„์˜ค๋Š” ํ˜•์‹์˜ ํ˜„์‹any ์ฃผ์„์„ ๊ฒ€์ถœํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผany์˜ ๋˜๋Œ์•„์˜ค๋Š” ํ˜•์‹๋„ ๊ฒ€์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
    // inferred "any" at ArrowFunctionReturn
    const abc = (param: string) => {
      switch (param) {
        case 'a':
          return true
        case 'b':
          return false
        case 'c':
          return '' as any
      }
    }
    

    as Expression(as any ๋‹จ์–ธ)


    as ๊ฒ€์‚ฌ ํ‚ค์›Œ๋“œ ๋‹จ์–ธ.
    function greet() {
      return 'hello' as any // at AsExpression
    }
    

    anycop.config.js


    ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์„ค์ •anycop.config.js์„ ํ†ตํ•ด ํŠน์ •ํ•œ ์„ค์ •์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.errorThrethold๋Š” ์˜ค๋ฅ˜ ๋ณด๊ณ ์„œ์˜ ์ž„๊ณ„๊ฐ’์ž…๋‹ˆ๋‹ค.Type Safe Coverage์˜ ํ•ฉ๊ณ„๊ฐ€ ์ด๋ณด๋‹ค ๋‚ฎ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ํ†ตํ•ฉ ๋„๊ตฌ ๋“ฑ์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.
    module.exports = {
      targetDir: ".",
      errorThrethold: 0.2
    }
    

    ๊ฒ€์‚ฌ ํ•ญ๋ชฉ ์ฆ๊ฐ€๋กœ ์ธํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ

    ts.SourceFile์€ts.Node์˜ ๋ฃจํŠธ ๋…ธ๋“œ๋กœ ์ด๋ฅผ ๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ๊ท€์† ํ•จ์ˆ˜(visit ํ•จ์ˆ˜)๋ฅผ ์žฌ๊ฒ€ํ† ํ–ˆ๋‹ค.ts.Node ๋ณด์œ ts.SyntaxKind, ์ด Syntax Kind๋กœ ์ฒ˜๋ฆฌ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
    function visit(node: ts.Node) {
      switch (node.kind) {
        case ts.SyntaxKind.VariableDeclaration:
          // ๅค‰ๆ•ฐๅฎฃ่จ€ใงใ‚ใ‚Œใฐ
          // ....ๅ‡ฆ็†
          break
        case ts.SyntaxKind.Parameter:
          // ๅผ•ๆ•ฐใงใ‚ใ‚Œใฐ
          // ....ๅ‡ฆ็†
          break
        case ts.SyntaxKind.BindingElement:
          // BindingElementใงใ‚ใ‚Œใฐ
          // ....ๅ‡ฆ็†
          break
        case ts.SyntaxKind.FunctionDeclaration:
          // ้–ขๆ•ฐๅฎฃ่จ€ใงใ‚ใ‚Œใฐ
          // ....ๅ‡ฆ็†
          break
        case ts.SyntaxKind.ArrowFunction:
          // ใ‚ขใƒญใƒผ้–ขๆ•ฐๅฎฃ่จ€ใงใ‚ใ‚Œใฐ
          // ....ๅ‡ฆ็†
          break
        case ts.SyntaxKind.AsExpression:
          // ใ‚ขใ‚ตใƒผใ‚ทใƒงใƒณใงใ‚ใ‚Œใฐ
          // ....ๅ‡ฆ็†
          break
      }
      ts.forEachChild(node, visit)
    }
    visit(source)
    

    ts.TypeChecker.getReturnTypeOfSignature


    '๋ถ€์ฃผ์˜ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜ํ˜•์ดany์— ๋น ์กŒ๋Š”๋ฐ...'๋ผ๋Š” ๊ฒ€์‚ฌ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.ts.SignatureDeclaration๋Š” ํ•จ์ˆ˜ ์„ฑ๋ช…์ด๋‚˜ ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.ts.TypeChecker์— ์žˆ๋Š” getSignatureFromDeclaration ํ•จ์ˆ˜์™€ getReturnTypeOfSignature ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ flags ํ•จ์ˆ˜๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.์ด์— ๋”ฐ๋ผ ts.TypeFlags.Any ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ Any ์ธ์ง€ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.
    // ts.ArrowFunction / ts.FunctionDeclaration ใฏ
    // ts.SignatureDeclaration ใฎใ‚ตใƒ–ใ‚ฟใ‚คใƒ—
    // node: ts.SignatureDeclaration
    const signature = checker.getSignatureFromDeclaration(node)
    if (signature) {
      const { flags } = checker.getReturnTypeOfSignature(signature)
      return checkAny(source, node, flags, name)
    }
    

    ์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ