출처 : 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.
"compileOnSaveDefinition": {
"properties": {
"compileOnSave": {
"description": "Enable Compile-on-Save for this project.",
"type": "boolean"
}
}
},
상속을 받아올 부모파일의 path를 적어준다
"extendsDefinition": {
"properties": {
"extends": {
"description": "Path to base configuration file to inherit from. Requires TypeScript version 2.1 or later.",
"type": "string"
}
}
},
// 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",
...
}
자동으로 검증된 설정 옵션이 적용된다
"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"
}
}
}
},
<outDir>
은 항상 제외한다 (include에 있어도)!리액트같은 라이브러리는 자바스크립트 기반이기 때문에 타이핑이 되어있지않다
👉 고로 타입을 관리해줄때는 TypeScript 이전에는 third-party 라이브러리를 통해 관리해왔었다
"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으로 사용이 되는 것.
"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"
}
"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$"
}
"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"
}
"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"
},
위 모든 옵션들을 켜게 되는 셈이다
function noImplicitAnyTestFunc(arg) {
console.log(arg);
}
// arg에 빨간 밑줄이 쳐지면서
// Parameter 'arg' implicity has an 'any' type
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;
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
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();
strictNullChecks 모드에서는, null 및 undefined 값이 모든 유형의 도메인에 속하지 않으며, 그 자신을 타입으로 가지거나 any일 경우에만 할당이 가능하다
한 가지 예외는 undefined에 void 할당 가능
strictNullChecks를 적용하지 않으면
모든 타입은 null, undefined 값을 가질 수 있다
👉 string으로 타입을 지정해도, null 혹은 undefined 값을 할당할 수 있다는 것
strictNullChecks를 적용한다면
strictNullChecks를 적용하지 않고, 어떤 값이 null과 undefined를 가진다는 것을 암묵적으로 인정하고 계속 사용하다보면 정확히 어떤 타입이 오는지 개발자 스스로가 간과할 수 있다
정말로 null과 undefined를 가질 수 있는 경우, 해당 값을 조건부로 제외하고 사용하는 것이 좋다
이 옵션을 켜고 사용한다면, 사용하려는 함수를 선언 시 부터 매개변수와 리턴 값에 정확한 타입을 지정하려는 노력을 기울여야 하고, 기울이게 될 것이다
함수 타입에 대한 bivariant 매개변수 검사를 비활성화한다
함수에서 인자로 다른 함수를 받을 때 그 함수 타입을 검사하는 방식으로 공변과 반병이 있다 (서브타입 글 참고)
인자 타입이 리턴 타입보다 범위가 넓어야한다
(Animal -> Greyhound) <: (Dog -> Dog)
반환 타입은 공변적 (covariant)
인자 타입은 반공변적 (contravariant)
그런데 TypeScript에서 인자 타입은 공변적이면서, 반공변적인게 문제 👉 이 문제를 해결하는 옵션이 strictFunctionTypes
strictFunctionTypes 옵션을 켜면 에러가 안나던걸 에러가 나게 한다
const button = document.querySelector('#id') as HTMLButtonElement;
button.addEventListener('keydown', (a: MouseEvent) => {});
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에서 안하는 경우
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);
}
}