본문 바로가기
JavaScript,TypeScript/TypeScript

TSC: 타입스크립트가 자바스크립트로 바뀌는 과정

by 그냥하는거지뭐~ 2024. 4. 30.
목차
- 프롤로그
- TSC Architecture
- Processing Overview
- 에러 처리

1. 프롤로그

자바스크립트는 개발자가 실수를 해도 종종 에러를 명시적으로 표시하지 않는다. 아래 예시를 보자. 

let result = "5" + 1;   // 결과: "51" 
let check = "5" == 5;   // 결과: true (동등 연산자는 타입을 강제 변환함)
let person = { name: "그냥하는거지뭐~" };
console.log(person.age);  // 결과: undefined (에러가 발생하지 않고 undefined 반환)

 
 
 이처럼 예측 가능성이 너무 떨어진다. 특히 서버에서 클라이언트로 데이터를 전송할 때나 React에서 데이터 흐름을 설계할 때, 데이터의 타입을 정확히 관리하는 것이 굉장히 중요한데, JS는 타입을 관리할 수 없으니 치명적이라고 할 수 있다.
 
 이러한 문제를 해결하기 위해 타입스크립트가 등장했다. TS는 JS의 superset으로, JS의 모든 기능을 포함하면서 정적 타이핑을 지원한다. TS는 JS로 변환되는 컴파일 과정 중에 코드 내의 타입 오류를 사전에 검사해서, 오류가 있는 경우 JS로 컴파일하지 않는다. 그래서 타입 에러를 사전에 잡아낼 수 있다. 누가 JS로 변환해 주는지, 어떤 과정을 통해 변환되고 오류를 잡아내는지 알아보도록 하자. 


2.  TSC Architecture

출처:  https://www.huy.rocks/everyday/04-01-2022-typescript-how-the-compiler-compiles

 

타입스크립트는 TSC라는 컴파일러를 통해 자바스크립트로 변환된다. 이때, TS와 JS 모두 high level 언어이기 때문에 트랜스파일러라고도 한다. typescript compiler는 src/compiler 폴더 안에 구현되어 있는데, 각각의 컴포넌트들이 ts 파일로 구현되어 있는 것을 볼 수 있다. 

 

TypeScript/src/compiler at main · microsoft/TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output. - microsoft/TypeScript

github.com

대표적으로 다음과 같은 컴포넌트들로 구성된다. 

- Scanner (scanner.ts)
- Parser (parser.ts)
- Binder (binder.ts)
- Checker (checker.ts)
- Emitter (emitter.ts)

 

Scanner

Scanner는 타입스크립트 소스 코드를 어휘적으로 분석(lexical analysis)하고 작은 단위로 나누어 토큰으로 변환한다. 

 

Parser

Parser는 scanner가 변환한 토큰을 이용하여 AST으로 변환한다. AST는 추상 구문 트리(Abstract Syntax Tree)로, 텍스트 기반의 코드를 컴파일러가 더 쉽게 처리할 수 있는 형태, 즉 노드 단위의 트리 형태로 만든다. 각각의 노드는 코드의 위치, 구문 종류, 코드 내용 등의 정보를 담고 있다. 

 

Binder

Binder는 Symbol이라는 데이터 구조를 생성하고, 해당 symbol에 대응하는 AST 노드를 스코프에 따라 바인딩한다. 이 과정에서 변수, 함수, 클래스 등의 선언과, 이를 코드의 어느 부분에서 사용하는지 파악하고 연결짓는 과정이 일어난다. 이러한 바인딩을 통해 타입 체킹과 스코프 관리를 가능하게 하고, 이름 충돌과 같은 문제를 방지하게 된다.  

 

Type Checker

Type Checker는 AST와 symbol을 이용하여 타입 검사를 하는데, getDiagnostics() 함수를 사용해서 타입을 검증하고 에러가 있으면 이를 저장한다.  

 

Emitter

Emitter는 타입 체크가 완료된 후 ts 코드를 js 코드로 변환한다. 개발자가 설정한 설정 파일 (tsconfig.json)을 읽어오고, emitFiles() 함수를 이용해 변환한다. 


3. Processing Overview

출처:  https://www.huy.rocks/everyday/04-01-2022-typescript-how-the-compiler-compiles

 

SourceCode ~~ scanner ~~> Token Stream
Token Stream ~~ parser ~~> AST
AST ~~ binder ~~> Symbols
AST + Symbols ~~ checker ~~> Type Validation
AST + Checker ~~ emitter ~~> JS

 

 


4. 에러 처리

컴파일러가 어느 단계에서 에러를 발견했는지에 따라 다른 에러가 반환된다. 

enum BuildResultFlags {
    None = 0,
    Success = 1 << 0,
    DeclarationOutputUnchanged = 1 << 1,
 
    ConfigFileErrors = 1 << 2,
    SyntaxErrors = 1 << 3,
    TypeErrors = 1 << 4,
    DeclarationEmitErrors = 1 << 5,
    EmitErrors = 1 << 6,
 
    AnyErrors = ConfigFileErrors | SyntaxErrors | TypeErrors | DeclarationEmitErrors | EmitErrors
}

 

tsconfig.json에 에러가 있다면 ConfigFileErrors, scanner에 의해 발견됐다면 SyntaxErrors가 반환된다. 

다음과 같이 구문은 맞지만, semantically incorrect하다면 parser나 type checker에 의해 TypeErrors가 반환될 것이다. 

let a: number = "hello";

 


Reference

- https://www.huy.rocks/everyday/04-01-2022-typescript-how-the-compiler-compiles

- https://radlohead.gitbook.io/typescript-deep-dive/overview

 

'JavaScript,TypeScript > TypeScript' 카테고리의 다른 글

TypeScript 100% 활용하기  (0) 2024.04.12