TypeScript Compiler

100pearlcent·2021년 8월 25일
1

TypeScript

목록 보기
3/6
post-thumbnail

Compilation Context

출처 : TypeScript Deepdive

The compilation context is basically just a fancy term for grouping of the files that TypeScript will parse and analyze to determnine what is valid and what isn't.

Along with the information about which files, the compilation context contains information about which compiler options are in use.

A great way to define this logical grouping (we also like to use the term project) is using a tsconfig.json file.

TsConfig Schema

TsConfig Schema 전체 보기

최상위(Top-level) 프로퍼티

  • compileOnSave
  • extends
  • compileOptions
  • files
  • include
  • exclude
  • references
  • typeAcquisition
  • tsNode

compileOnSave

        "compileOnSaveDefinition": {
            "properties": {
                "compileOnSave": {
                    "description": "Enable Compile-on-Save for this project.",
                    "type": "boolean"
                }
            }
        },
  • true / false (default false)
  • Visual Studio 2015 with TypeScript 1.8.4 이상
  • atom-typescript 플러그인

extends

상속을 받아올 부모파일의 path를 적어준다

"extendsDefinition": {
            "properties": {
                "extends": {
                    "description": "Path to base configuration file to inherit from. Requires TypeScript version 2.1 or later.",
                    "type": "string"
                }
            }
        },
  • 파일 (상대)경로명: string
  • TypeScript 2.1 New Spec
// in PROJECT/base.json
{
	"compilerOptions": {
    	"strict": true
    }
}

// in PROJECT/tsconfig.json
{
	"extends": "./base.json",
}

터미널에 npm install --save-dev @tsconfig/deno를 입력하면

{
	"extends": "@tsconfig/deno/tsconfig.json",
      ...
}

자동으로 검증된 설정 옵션이 적용된다

files, include, exclude

  • 어느 디렉토리의 어느 파일을 컴파일 할지 결정하게 됨
  • 위 설정이 없다면 컴파일러는 모든 파일을 컴파일 하려 할 것
 "filesDefinition": {
            "properties": {
                "files": {
                    "description": "If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. When a 'files' property is specified, only those files and those specified by 'include' are included.",
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "excludeDefinition": {
            "properties": {
                "exclude": {
                    "description": "Specifies a list of files to be excluded from compilation. The 'exclude' property only affects the files included via the 'include' property and not the 'files' property. Glob patterns require TypeScript version 2.0 or later.",
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "includeDefinition": {
            "properties": {
                "include": {
                    "description": "Specifies a list of glob patterns that match files to be included in compilation. If no 'files' or 'include' property is present in a tsconfig.json, the compiler defaults to including all files in the containing directory and subdirectories except those specified by 'exclude'. Requires TypeScript version 2.0 or later.",
                    "type": "array",
                    "uniqueItems": true,
                    "items": {
                        "type": "string"
                    }
                }
            }
        },

files

  • 상대 혹은 절대 경로의 리스트 배열
  • exclude 보다 우선순위가 높음

include, exclude

  • glob 패턴 (마치 .gitignore)

include

  • exclude 보다 후순위
  • 별표를 사용하면 .ts/.tsx/.d.ts만 inlucde (allowJS)

exclude

  • 설정 안하면 4가지(node_modules, bower_components, jspm_packages, )를 default로 제외
  • <outDir> 은 항상 제외한다 (include에 있어도)!

compileOptions - typeRoots, types

type ❓

리액트같은 라이브러리는 자바스크립트 기반이기 때문에 타이핑이 되어있지않다
👉 고로 타입을 관리해줄때는 TypeScript 이전에는 third-party 라이브러리를 통해 관리해왔었다

compileOptions : type

 "typeRoots": {
                            "description": "Specify multiple folders that act like `./node_modules/@types`.",
                            "type": "array",
                            "uniqueItems": true,
                            "items": {
                                "type": "string"
                            },
                            "markdownDescription": "Specify multiple folders that act like `./node_modules/@types`.\n\nSee more: https://www.typescriptlang.org/tsconfig#typeRoots"
                        },
                        "types": {
                            "description": "Specify type package names to be included without being referenced in a source file.",
                            "type": "array",
                            "uniqueItems": true,
                            "items": {
                                "type": "string"
                            },
                            "markdownDescription": "Specify type package names to be included without being referenced in a source file.\n\nSee more: https://www.typescriptlang.org/tsconfig#types"
                        },  

e.g.) ts파일 상단에 import React from "react";를 적고 마우스 오버를 하면 Try npm i --save-dev @types/react 문구가 뜬다.
설치하면 node_modules > @types > react > index.d.ts 파일이 생성이 되는데 이것이 리액트의 type definition으로 사용이 되는 것.

types

  • TypeScript 2.0 부터 사용 가능해진 내장 type definition 시스템
  • 아무 설정을 안하면? > node_modules/@types 라는 모든 경로를 찾아서 사용
  • typeRoots를 사용하면? > 배열 안에 들어있는 경로들 아래서만 가져옴
  • types를 사용하면? > 배열 안의 모듈 혹은 ./node_modules/@types 안의 모듈 이름에서 찾아온다 / [] 빈 배열을 넣는다는 것은 이 시스템을 이용하지 않겠다는 뜻
  • typeRoots와 types를 같이 사용하지 않는다

compileOptions - target과 lib

프로젝트 설정에 있어서 가장 기본이 되는 요소

compileOptions: target

  • 빌드의 결과물을 어떤 버전으로 할 것인가
  • 지정하지 않았을 시 es3
"target": {
                            "description": "Set the JavaScript language version for emitted JavaScript and include compatible library declarations.",
                            "type": "string",
                            "default": "ES3",
                            "anyOf": [
                                {
                                    "enum": [
                                        "ES3",
                                        "ES5",
                                        "ES6",
                                        "ES2015",
                                        "ES2016",
                                        "ES2017",
                                        "ES2018",
                                        "ES2019",
                                        "ES2020",
                                        "ES2021",
                                        "ESNext"
                                    ]
                                },
                                {
                                    "pattern": "^([Ee][Ss]([356]|(20(1[56789]|2[01]))|[Nn][Ee][Xx][Tt]))$"
                                }
                            ],
                            "markdownDescription": "Set the JavaScript language version for emitted JavaScript and include compatible library declarations.\n\nSee more: https://www.typescriptlang.org/tsconfig#target"
                        }  

compileOptions: lib

  • 어떤 기본 type definition 라이브러리를 사용할 것인가
  • lib 지정하지 않았을 시
    • target이 'es3'이고, 디폴트로 lib.d.ts를 사용
    • target이 'es5'이면, 디폴트로 dom, es5, scripthost를 사용
    • target이 'es6'이면, 디폴트로 dom, es6, dom.iterable, scripthost를 사용
  • lib를 지정하면 그 lib 배열로만 라이브러리를 사용한다
    • 빈 [] => 'no definition found . . .'
"lib": {
                            "description": "Specify a set of bundled library declaration files that describe the target runtime environment.",
                            "type": "array",
                            "uniqueItems": true,
                            "items": {
                                "type": "string",
                                "anyOf": [
                                    {
                                        "enum": [
                                            "ES5",
                                            "ES6",
                                            "ES2015",
                                            "ES2015.Collection",
                                            "ES2015.Core",
                                            "ES2015.Generator",
                                            "ES2015.Iterable",
                                            "ES2015.Promise",
                                            "ES2015.Proxy",
                                            "ES2015.Reflect",
                                            "ES2015.Symbol.WellKnown",
                                            "ES2015.Symbol",
                                            "ES2016",
                                            "ES2016.Array.Include",
                                            "ES2017",
                                            "ES2017.Intl",
                                            "ES2017.Object",
                                            "ES2017.SharedMemory",
                                            "ES2017.String",
                                            "ES2017.TypedArrays",
                                            "ES2018",
                                            "ES2018.AsyncGenerator",
                                            "ES2018.AsyncIterable",
                                            "ES2018.Intl",
                                            "ES2018.Promise",
                                            "ES2018.Regexp",
                                            "ES2019",
                                            "ES2019.Array",
                                            "ES2019.Object",
                                            "ES2019.String",
                                            "ES2019.Symbol",
                                            "ES2020",
                                            "ES2020.BigInt",
                                            "ES2020.Promise",
                                            "ES2020.String",
                                            "ES2020.Symbol.WellKnown",
                                            "ESNext",
                                            "ESNext.Array",
                                            "ESNext.AsyncIterable",
                                            "ESNext.BigInt",
                                            "ESNext.Intl",
                                            "ESNext.Promise",
                                            "ESNext.String",
                                            "ESNext.Symbol",
                                            "DOM",
                                            "DOM.Iterable",
                                            "ScriptHost",
                                            "WebWorker",
                                            "WebWorker.ImportScripts",
                                            "Webworker.Iterable",
                                            "ES7",
                                            "ES2021",
                                            "ES2020.SharedMemory",
                                            "ES2020.Intl",
                                            "ES2021.Promise",
                                            "ES2021.String",
                                            "ES2021.WeakRef",
                                            "ESNext.WeakRef"
                                        ]
                                    },
                                    {
                                        "pattern": "^[Ee][Ss]5|[Ee][Ss]6|[Ee][Ss]7$"
                                    }  

compileOptions - outDir, outFile, rootDir

"outFile": {
                            "description": "Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output.",
                            "type": "string",
                            "markdownDescription": "Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output.\n\nSee more: https://www.typescriptlang.org/tsconfig#outFile"
                        },
                        "outDir": {
                            "description": "Specify an output folder for all emitted files.",
                            "type": "string",
                            "markdownDescription": "Specify an output folder for all emitted files.\n\nSee more: https://www.typescriptlang.org/tsconfig#outDir"
                        },
"rootDir": {
                            "description": "Specify the root folder within your source files.",
                            "type": "string",
                            "markdownDescription": "Specify the root folder within your source files.\n\nSee more: https://www.typescriptlang.org/tsconfig#rootDir"
                        }

compileOptions - strict

  • strict는 보통 true로 설정하는 편이 좋다
"strict": {
                            "description": "Enable all strict type checking options.",
                            "type": "boolean",
                            "default": false,
                            "markdownDescription": "Enable all strict type checking options.\n\nSee more: https://www.typescriptlang.org/tsconfig#strict"
                        },  

Enable all strict type checking options

  • --noImplicitAny
  • --noImplicitThis
  • --strictNullChecks
  • --strictFunctionTypes
  • --strictPropertyInitialization
  • --strictBindCallApply
  • --alwaysStrict

위 모든 옵션들을 켜게 되는 셈이다

--noImplicitAny

  • Raise error on expressions and declarations with an implied any type
  • 명시적이지 않게 any 타입을 사용하여, 표현식과 선언에 사용하면 에러를 발생시킴
function noImplicitAnyTestFunc(arg) {
	console.log(arg);  
} 
  
// arg에 빨간 밑줄이 쳐지면서
// Parameter 'arg' implicity has an 'any' type
  • TypeScript가 추론을 실패한 겨우, any가 맞으면 any라고 지정하라
  • 아무것도 쓰지 않으면 에러 발생
  • 이 오류를 해결하면, any라고 지정되어 있지 않은 경우는 any가 아닌 것이다 (타입 추론이 되었으므로)

suppressImplicitAnyIndexErrors

noImplicitAny 사용 시, 인덱스 객체에 인덱스 시그니쳐가 없는 경우 오류가 발생하는 이를 예외처리한다

var obj = {
	bar: 10  
};
  
obj['foo'] = 10; // Error: Index signature of object type implicitly has an 'any; type
obj['bar'] = 10; // OK
obj.baz = 10;
  • obj['foo']로 사용할 때, 인덱스 객체라 판단하여 타입에 인덱스 시그니쳐가 없는 경우 에러 발생시킴
  • 이 때 suppressImplicitAnyIndexErrors 옵션을 사용하면, 이런 경우 예외로 간주하여 에러를 발생시키지 않음

--noImplicitThis

  • Raise error on this expressions with an implied any type
  • 명시적이지 않게 any 타입을 사용하여 this 표현식에 사용하면 에러를 발생시킨다
function noImplicitThisTestFunc(name: string, age: number) {
	this.name = name;
  	this.age = age;
  
  	return this;
} 
  
// this에 빨간줄이 쳐지면서
// 'this' implictly has type 'any' because it does not have a type annotation

// '이 this엔 어떤 형태만 가능하다'라고 제한을 해주어야한다
function noImplicitThisTestFunc(this, name: string, age: number) {
	this.name = name;
  	this.age = age;
  
  	return this;
  
// 매개변수 this에 빨간줄이 쳐지면서
// Parameter 'this' implicitly has an 'any' type
  • 첫 번째 매개변수 자리에 this를 놓고, this에 대한 타입을 지정해주지않으면 noImplicitAny가 오류를 발생시킨다
  • JavaScript에서는 매개변수에 this를 넣으면, 이미 예약된 키워드라 SyntaxError를 발생시키지만 TypeScript에서는 괜찮다
  • call / apply / bind 와 같이 this를 대체하여 함수 콜을 하는 용도로도 쓰인다
  • 그래서 this를 any로 명시적으로 지정하는 것은 합리적이다 (물론 구체적인 사용처가 있는 경우 타입을 표현하기도 한다)
class NoImplicitThisTestClass {
	private _name: string;
  	private _age: number;
 
  constructor(name: string, age:number) {
  	this._name = name;
  	this._age = age;
  }
  
  public print(this: NoImplicitThisTestClass) {
  	console.log(this._name, this._age);
  }
} 
  
new NoImplicitThisTestClass('Jinju', 26).print();
  • Class에서는 this를 사용하면서, noImplicitThis와 관련한 에러가 나지 않는다 (당연)
  • Class에서 constructor를 제외한 멤버함수(a.k.a. 메서드)의 첫번째 매개변수도 일반 함수와 마찬가지로 this를 사용할 수 있다

--strictNullChecks

  • strictNullChecks 모드에서는, null 및 undefined 값이 모든 유형의 도메인에 속하지 않으며, 그 자신을 타입으로 가지거나 any일 경우에만 할당이 가능하다

  • 한 가지 예외는 undefined에 void 할당 가능

  • strictNullChecks를 적용하지 않으면

    모든 타입은 null, undefined 값을 가질 수 있다
    👉 string으로 타입을 지정해도, null 혹은 undefined 값을 할당할 수 있다는 것

  • strictNullChecks를 적용한다면

    • 모든 타입은 null, undefined 값을 가질 수 없고, 가지려면 union type을 이용해서 직접 명시해야한다
    • any 타입은 null과 undefined를 가진다. 예외적으로 void 타입의 경우 undefined를 가진다
  • strictNullChecks를 적용하지 않고, 어떤 값이 null과 undefined를 가진다는 것을 암묵적으로 인정하고 계속 사용하다보면 정확히 어떤 타입이 오는지 개발자 스스로가 간과할 수 있다

  • 정말로 null과 undefined를 가질 수 있는 경우, 해당 값을 조건부로 제외하고 사용하는 것이 좋다

  • 이 옵션을 켜고 사용한다면, 사용하려는 함수를 선언 시 부터 매개변수와 리턴 값에 정확한 타입을 지정하려는 노력을 기울여야 하고, 기울이게 될 것이다

--strictFunctionTypes

  • 함수 타입에 대한 bivariant 매개변수 검사를 비활성화한다

  • 함수에서 인자로 다른 함수를 받을 때 그 함수 타입을 검사하는 방식으로 공변과 반병이 있다 (서브타입 글 참고)

  • 인자 타입이 리턴 타입보다 범위가 넓어야한다
    (Animal -> Greyhound) <: (Dog -> Dog)

  • 반환 타입은 공변적 (covariant)

  • 인자 타입은 반공변적 (contravariant)

  • 그런데 TypeScript에서 인자 타입은 공변적이면서, 반공변적인게 문제 👉 이 문제를 해결하는 옵션이 strictFunctionTypes

  • strictFunctionTypes 옵션을 켜면 에러가 안나던걸 에러가 나게 한다

const button = document.querySelector('#id') as HTMLButtonElement;
  
button.addEventListener('keydown', (a: MouseEvent) => {});
  • 옵션을 켜기 전에는 위 코드는 에러를 발생시키지 않지만, 이제는 에러를 발생시킨다

--strictPropertyInitialization

  • 정의되지 않은 클래스의 속성이 생성자에서 초기화되었는지 확인한다
  • 이 옵션을 사용하려면 --strictNullChecks를 같이 켜주어야한다
class Person {
	private _name: string;
  	private _age: number;
  	
  	// _name, _age에 빨간줄이 쳐지면서
  	// Property '_age' has no initializer and is not definitely assigned in the constructor
  	// 선언은 되었지만 초기화(값이 할당)되지 않았다고 알려줌
  
  constructor() {}
  
  public print() {
  	console.log(this._name, this._age);
  }
}  
class Person {
	private _name: string;
  	private _age: number;
  
  constructor(name: string, age: number) {
  	this._name = name;
  	this._age = age;
  
   // 이렇게 값을 할당해주면 오류가 나지 않는다
  }
  
  public print() {
  	console.log(this._name, this._age);
  }
}  

constructor에서 안하는 경우

  • 보통 다른 함수로 이니셜라이즈 하는 경우 (async 함수)
  • constructor에는 async를 사용할 수 없다
class Person {
	private _name!: string;
  	private _age!: number;
  // 클래스 자체에서는 위 값에 값이 할당됐는지 보장할 수 없으므로
  // 개발자가 ! 를 붙여서 안심(?)시키며 에러를 무시가능
  
  public async initialize(name: string, age: number) {
  this._name = name;
  this._age = age;
  }
  
  public print() {
  	console.log(this._name, this_age);
  }
}  

--strictBindCallApply

  • bind, call, apply에 대한 엄격한 검사 수행
  • function의 내장 함수인 bind / call / apply를 사용할 때, 엄격하게 타입을 체크하도록 하는 옵션
  • bind는 해당 함수 안에서 사용할 this와 인자를 설정해주는 역할을 하고, call과 apply는 this와 인자를 설정한 후 실행까지 한다
  • call 과 apply는 인자를 설정하는 방식에서 차이점이 있다
    • call은 함수의 인자를 여러 인자의 나열로 넣어서 사용하고, apply는 모든 인자를 배열 하나로 넣어서 사용한다

--alwaysStrict

  • 각 소스파일에 대해 JavaScript의 strict mode로 코드를 분석하고, "엄격하게 사용"을 해제한다
  • 컴파일된 JavaScript 파일에 "use strict"가 추가 됨

0개의 댓글