Typescript 컴파일러로 코드 식별

20155 단어 typescriptjavascript
Typescript가 대중화되고 유형이 지정된 코드를 식별하기 쉽기 때문에 AST를 기반으로 하는 대신 Typescript 컴파일러로 코드 자동화를 수행하는 것이 때때로 좋은 생각이라는 것을 알았습니다.

예를 들어 AST로 모든 React 함수 구성 요소를 찾으려고 할 때 다음과 같이 단순화된 코드와 같이 Babel과 같은 도구의 도움이 필요할 수 있습니다.

import { transformSync, Visitor } from "@babel/core";
import { Identifier } from "@babel/types";

const funcs = [];

transformSync(content, {
  parserOpts: {
    plugins: ["jsx", "typescript", "classProperties", "optionalChaining"],
  },
  plugins: [
    () => ({
      visitor: {
        ExportDefaultDeclaration({ node: { declaration } }) {
          switch (declaration.type) {
            case "FunctionDeclaration":
            case "ArrowFuntionExpression":
              funcs.push("default");
              break;
          }
        },
        ExportNamedDeclaration({ node }) {
          const { declaration } = node;
          if (declaration) {
            switch (declaration.type) {
              case "FunctionDeclaration":
                if (declaration.id) {
                  funcs.push(declaration.id.name);
                }
                break;
              case "VariableDeclaration":
                declaration.declarations.forEach((d) => {
                  if (!d.id.name) return;
                  switch (d.init?.type) {
                    case "ArrowFunctionExpression":
                      funcs.push(d.id.name);
                      break;
                  }
                });
                break;
            }
          }
        },
      },
    }),
  ],
});
console.log(funcs);


Here my little VSCode plugin Babel AST Explorer helped me saved a lot of time.



Visitors는 다음 코드만 인식할 수 있습니다.

export default function f() {}



export default () => {};



export function f() {}



export const f = () => {};


변수에 할당되면 식별하기가 더욱 어려워집니다. 예를 들어:

function f() {}
export { f };
export const f2 = f;
export default f;


이것은 저를 Typescript 컴파일러로 이끌었습니다.

그러나 document of Typescript compiler은 도움이 되지 않습니다. 작은 프로젝트에서 테스트한 Typescript 컴파일러 사용에 대한 몇 가지 참고 사항이 있습니다. 시간을 절약할 수 있기를 바랍니다.
  • The basic definitions
  • Initiating the type checking utility
  • Finding the exports of an ES module
  • Printing the closest type name
  • Identifying the type syntax with flags
  • Identifying the Node type with ts helpers
  • Get Type object of a function

  • 기본 정의



    Typescript 컴파일러는 몇 가지 기본 개체를 정의하며 저는 종종 Node , Symbol , Type 이 세 가지 개체를 사용하여 작업했습니다.

    내 이해로는
  • Symbol는 Typescript가 컴파일러에서 통신하는 데 사용하는 주요 데이터 구조입니다.
  • Node는 토큰, 캐릭터 위치 등을 처리하는 AST 노드의 "상위 집합"입니다.
  • Type는 유형 안전성을 진단하기 위한 Typescript "유형"입니다.

  • 따라서 우리는 항상 Symbol을 먼저 가져와야 하고, 필요하다면 Node나 Type을 요청해야 합니다.

    예를 들어 다음과 같이 소스 코드에서 Symbol을 가져올 수 있습니다.

    checker.getSymbolAtLocation(source);
    


    여기서 "검사기"가 무엇인지 궁금할 수 있습니다. 다음 섹션에서 이에 대해 설명하겠습니다.

    기호를 사용하면 다음과 같이 노드 유형에 따라 해당 노드를 찾을 수 있습니다.

    symbol.declarations;
    symbol.valueDeclaration;
    


    유형으로 작업해야 하는 경우 Symbol 및 Node를 사용하여 얻을 수 있습니다.

    checker.getTypeOfSymbolAtLocation(mySymbol, myNode);
    


    또는 Node 유형에서 직접 가져옵니다.

    checker.getTypeFromTypeNode(myNode);
    


    유형 검사 유틸리티 시작



    선언의 유형을 파악하려면 먼저 Typescript 유형 검사기를 시작하고 소스 코드를 Typescript 기호로 구문 분석해야 합니다.

    import ts from "typescript";
    import assert from "assert/strict";
    
    const program = ts.createProgram({
      rootNames: [file], // A set of root files
      options: {}, // The compiler options
      // There are 3 other options available but I am not sure what they are used for yet
    });
    const source = program.getSourceFile(file);
    assert(source);
    const checker = program.getTypeChecker();
    const moduleSymbol = checker.getSymbolAtLocation(source);
    assert(moduleSymbol);
    


    ES 모듈의 내보내기 찾기



    위에서 얻은 유형 검사기와 기호를 사용하면 ES 모듈 내보내기에서 분석을 계속하는 것이 Babel보다 훨씬 쉽습니다.

    const exports = checker.getExportsOfModule(moduleSymbol);
    exports.forEach((ex) => {
      ex.declarations.forEach((d) => {
        // ...
      });
    });
    


    가장 가까운 유형 이름 인쇄



    유형 검사기는 헬퍼와 가장 가까운 유형 이름을 제공할 수 있습니다.

    const myType = checker.getTypeOfSymbolAtLocation(
      mySymbol,
      mySymbol.valueDeclaration!
    );
    checker.typeToString(myType);
    


    글쎄요, 이 이름은 우리가 종종 유니온 또는 확장 유형을 사용하기 때문에 이 식별자가 우리가 찾고 있는 올바른 유형인지 식별하는 데 솔직히 별로 도움이 되지 않습니다. 불행히도 아직 더 나은 방법을 찾지 못했습니다. 내가 생각할 수 있는 한 가지 가능성은 코드 조각을 만들고 Typescript에 요청하여 전체 프로젝트에서 코드를 진단하는 것입니다.

    플래그로 유형 구문 식별



    Typescript 컴파일러는 SymbolFlags , SyntaxKind , ModifierFlags 등 여러 플래그를 정의했습니다. 가장 유용한 플래그는 SyntaxKind 입니다. SyntaxKind는 다음과 같은 방식으로 Node에서 유형 키워드를 식별하는 데 도움이 될 수 있습니다.

    if (myNode.kind === ts.SyntaxKind.NullKeyword) {
      // This is a null type, do something
    }
    

    ModifierFlags와 같은 일부 플래그에는 약간 더 많은 작업이 필요합니다.

    if (ts.getCombinedModifierFlags(myNode) & (ts.ModifierFlags.Export !== 0);) {
      // I am exported, do something
    }
    


    ts 헬퍼로 노드 유형 식별



    컴파일러는 어떤 종류의 코드를 다루고 있는지 식별하기 위한 일련의 도우미를 제공합니다. 예를 들어:

    ts.isFunctionDeclaration(myNode);
    ts.isArrowFunction(myNode);
    ts.isVariableDeclaration(myNode);
    ts.isIdentifier(myNode);
    // ... and you can get more from auto-complete
    


    이러한 도우미는 Node 와 함께 작동하며 구문 정의에 익숙한 경우 이름에서 매우 간단해 보입니다. 그러나 도우미 목록이 너무 길어서 Typescript가 ECMA, JSDoc 및 해당 타이핑 작업을 시도할 때 올바른 도우미를 찾을 수 없습니다.

    함수의 Type 개체 가져오기



    경우에 따라 함수 또는 구문의 매개 변수 및 반환 유형을 알고 싶을 수 있습니다. 이를 달성하기 위해 Signature 개체에서 가져올 수 있는 Type 개체에서 시작합니다.

    checker
      .getTypeOfSymbolAtLocation(mySymbol, mySymbol.valueDeclaration)
      .getConstructSignatures()
      .map((signature) => ({
        parameters: signature.parameters.map((paramSymbol) =>
          checker.getTypeOfSymbolAtLocation(
            paramSymbol,
            paramSymbol.valueDeclaration
          )
        ),
        returnType: signature.getReturnType(), // returns Type object
      }));
    

    좋은 웹페이지 즐겨찾기