본문 바로가기
Tech/Typescript

tsconfig.json을 설정해보자

by egas 2021. 8. 19.

 

Typescript는 동적 타입 언어인 Javascript를 타입이 있는 언어로 사용할 수 있게 해주는 언어이다. Typescript로 작성된 코드는 트랜스파일러(컴파일러)를 통해 Javascript로 변환된다. 아래 명령어로 컴파일을 한다.

 

tsc index.ts

 

컴파일 과정에서 여러 옵션을 정할 수 있는데, 해당 옵션들을 작성하는 파일이 tsconfig.json 이다.

 

Typescript에 가장 기본이 되는 tsconfig.json 파일을 설정해보자. 디렉토리에 tsconfig.json 파일이 있다면 해당 디렉토리가 Typescript 프로젝트의 루트가 된다. tsconfig.json 파일은 프로젝트를 컴파일하는 데 필요한 루트 파일과 컴파일러 옵션을 지정한다.

 

설치하기

npx tsc --init

 

tsconfig.json Options 

 tsconfig.json 옵션은 아래 사이트에서 확인 가능하다.

https://www.typescriptlang.org/tsconfig

 

TSConfig Reference - Docs on every TSConfig option

From allowJs to useDefineForClassFields the TSConfig reference includes information about all of the active compiler flags setting up a TypeScript project.

www.typescriptlang.org

 

Root Fields

  • files, extends, include, exclude, references

다음은 Root 옵션들이다. 기본 path는  tsconfig.json 이 있는 곳으로 부터의 상대 경로이다. include및 exclude는 패턴을 명시할 때 glob pattern을 사용한다. 

 

  • * : 0개 이상의 문자와 일치(디렉토리 구분 기호 제외)
  • ? : 임의의 한 문자와 일치합니다(디렉토리 구분 기호 제외).
  • **/ : 모든 레벨에 중첩된 모든 디렉토리와 일치
  • 만약 glob pattern에 파일 확장자가 포함되어있지 않으면, .ts, .tsx, .d.ts 확장자만 기본값으로 포함한다. (.js 와 .jsx는 allowJS 옵션을 활성화시켜야 포함할 수 있다.)

 

- files

프로그램에 포함시킬 파일의 허용 목록을 지정한다. 파일을 찾을 수 없으면 오류가 발생한다. 기본값은 false이다.

 

이것은 적은 수의 파일만 있고 많은 파일을 참조하기 위해 glob을 사용할 필요가 없을 때 유용하다. 보통의 경우는 includes를 사용하자.

 

예시:

{
  "compilerOptions": {},
  "files": [
    "core.ts",
    "sys.ts",
    "types.ts",
    "scanner.ts",
    "parser.ts",
    "utilities.ts",
    "binder.ts",
    "checker.ts",
    "tsc.ts"
  ]
}

 

- extends

tsconfig 에 존재하는 옵션을 상속 받을 수 있다. 기본값은 false이다.

 

extends가 순환적으로 참조하는 것은 허용하지 않으며, 기본 파일이 먼저 로드된다음, 설정파일들이 덮어씌어진다.

 

예시:

// tsconfig.json
{
  "extends": "./configs/base"
}

 

// configs/base.json
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

 

- include

컴파일 할때 포함할 파일 이름의 배열이나 패턴을 명시한다.

 

files 옵션이 설정되어있을때,

[]

그 이외의 경우는 

["**/*"]

을 기본 값으로 갖는다.

 

예시:

{
  "include": ["src/**/*", "tests/**/*"]
}

일 경우,

.
├── scripts                ⨯
│   ├── lint.ts            ⨯
│   ├── update_deps.ts     ⨯
│   └── utils.ts           ⨯
├── src                    ✓
│   ├── client             ✓
│   │    ├── index.ts      ✓
│   │    └── utils.ts      ✓
│   ├── server             ✓
│   │    └── index.ts      ✓
├── tests                  ✓
│   ├── app.test.ts        ✓
│   ├── utils.ts           ✓
│   └── tests.d.ts         ✓
├── package.json
├── tsconfig.json
└── yarn.lock

 

- exclude

컴파일 할때 제외할 파일 이름의 배열이나 패턴을 명시한다. (단, include에 포함되는 범위 안에서만 제외된다.)

 

아래 값을 기본값으로 갖는다.

["node_modules", "bower_components", "jspm_packages"]

 

추가적으로 outDir 값이 명시되어있으면, outDir에 명시된 값도 포함한다.

 

예시:

{
  "include": ["src/**/*", "tests/**/*"],
  "exclude": ["tests/modules/*"]
}

 

 

- references

references는 TypeScript 프로그램을 더 작은 조각으로 구성하는 방법이다. 빌드 및 편집기 상호 작용 시간을 크게 개선하고, 구성 요소 간의 논리적 분리를 할 수 있다.

 

예시:

{
    "compilerOptions": {
        // The usual
    },
    "references": [
        { "path": "../src" }
    ]
}

Compiler Options

컴파일 옵션을 설정 할 수 있다. 경고의 경우, editor에서만 표기되는 경고이다. 따로 터미널에서 컴파일시 경고가 발생하지 않는다.

 

allowUnreachableCode

연결할 수 없는 코드 허용에 대한 옵션이다.

  • undefined: (기본값) 편집자에게 경고로 제안 제공.
  • true: 연결할 수 없는 코드는 무시.
  • false: 연결할 수 없는 코드에 대한 컴파일러 오류 발생.

예시:

"allowUnreachableCode": false일 경우 컴파일러 오류 발생.

function fn(n: number) {
  if (n > 5) {
    return true;
  } else {
    return false;
  }
  return true;
>> Unreachable code detected.
}

 

allowUnusedLabels

사용하지않는 레이블 허용에 대한 옵션이다.

  • undefined: (기본값) 편집자에게 경고로 제안 제공.
  • true: 사용하지 않는 레이블은 무시.
  • false: 사용하지 않는 레이블에 대한 컴파일러 오류 발생.

 

예시:

function verifyAge(age: number) {
  // Forgot 'return' statement
  if (age > 18) {
    verified: true;
>> Unused label.
  }
}

 

exactOptionalPropertyTypes (typscript v4.4 이후부터 사용가능)

옵셔널 프리픽스를 사용한 값에 undefined를 할당하는 것을 허용하는 옵션이다.

  • true: 옵셔널 프리픽스에 undefined를 할당하는 것을 금지.
  • false: (기본값) 옵셔널 프리픽스를 사용한 값에 undefined를 할당하는 것을 허용.

 

interface UserDefaults {
  // The absence of a value represents 'system'
  colorThemeOverride?: "dark" | "light";
}

color 프로퍼티는 ? (옵셔널 프리픽스)와 함께 선언되었기 때문에, 해당 프로퍼티가 인터페이스 내에 존재할 수도 있고 없을 수도 있다는 것을 표현하고 있다. 이 프로퍼티의 타입은 기본적으로 dark | light | undefiend로 평가된다.

 

여기서 의도하지 않은 결과가 발생한다. 옵셔널 프로퍼티는 사실 "이 프로퍼티가 존재할 수도 있고 존재하지 않을 수도 있어"의 의미인데, 의도와는 다른 "이 프로퍼티가 존재는 하는데 값이 undefined야"의 의미를 충족할 수 있기 때문이다. 

const user: UserDefaults = {
  color: undefined, // dark | light | undefined
};

 

exactOptionalPropertyTypes 옵션은 이런 에러를 방지해준다.

const user: UserDefaults = {
>> Type 'undefined' is not assignable to type '"light" | "dark"'.ts(2322)
  color: undefined,
};

Additional Checks

 

noFallthroughCasesInSwitch

switch 문에서 break나 return 없이 다음 것으로 통과하는것을 허용할지에 대한 옵션이다.

  • true: switch 문 내에 Fallthrough 케이스가 존재하면 경고 발생
  • false: (기본 값) switch 문 내에 Fallthrough 케이스가 존재해도 무시한다.

switch 문을 종료시키지 않고 다음 케이스로 그냥 흘려버리는 케이스를 Fallthrough case라고 한다.  C++이나 Swift 같은 언어에서는 이런 케이스에 명시적으로 [[fallthrough]]와 같은 키워드를 작성해서 명시하는데, javascript는 문법이 없어서 실수가기가 쉽다.

 

Typescript는 Fallthrough case가 발생했을 때 경고를 보여주는 noFallthroughCasesInSwitch 옵션을 제공한다.

const fallthrough = 6;

switch (fallthrough) {
  case 1:
>> Fallthrough case in switch.ts(7029)
    console.log(1);
  case 2:
    console.log(2);
    break;
  default:
    break;
}

 

noImplicitOverride

자식 클래스의 암묵적인 오버라이딩을 허용할지에 대한 옵션이다.

  • true: 자식 클래스의 암묵적인 오버라이딩을 금지한다.
  • false: (기본 값) 자식 클래스의 암묵적인 오버라이딩을 허용한다.

암묵적인 오버라이딩이 허용된다면, 개발자가 인지하지 못한채 슈퍼 클래스의 기능을 오버라이딩 해버리는 상황이 발생할 수 있다. 개발자가 의도치 않게 슈퍼 클래스의 기능을 변경할 수 있으므로 해당 옵션이 존재한다.

class weapon {
  reload() {}
  attack() {}
}
 
class gun extends weapon {
  // This member must have an 'override' modifier because it overrides a member in the base class 'Album'.ts(4114)
  reload() {}

  // overriding 키워드를 사용한 명시적 오버라이딩은 허용된다.
  override attack() {}
}

 

noImplicitReturns

함수의 모든 코드 경로를 검사하여 값을 반환하는지 확인한다.

  • true: 암묵적인 undefined 반환을 금지한다.
  • false: (기본 값) 암묵적인 undefined 반환을 허용한다.
function lookupHeadphonesManufacturer(color: 'blue' | 'black'): string {
  if (color === 'blue') {
    return 'beats';
  } else {
    ('bose');
  }
}

 

noPropertyAccessFromIndexSignature

  • true: 암묵적인 undefined 반환을 금지.
  • false: (기본 값) 암묵적인 undefined 반환을 허용.

이 플래그가 없으면 TypeScript에서 점 구문을 사용하여 정의되지 않은 필드에 접근할 수 있으므로, 인터페이스나 타입에 미리 정의되지 않은 프로퍼티에 . 문법을 사용하여 접근하는 것을 금지한다.

 

아예 정의되지 않은 프로퍼티에 접근하는 상황은 당연히 에러가 나야한다. 위 옵션은 조금 더 애매한 상황에 대한 옵션이다.

interface GameSettings {
  speed: 'fast' | 'medium' | 'slow';
  quality: 'high' | 'low';
  [key: string]: string;
}

 

다음 인터페이스의 경우 [key: string]: string 의 인덱스 시그니처 프로퍼티를 가지고 있다. 키의 타입과 값의 타입이 string 타입이면 허용하겠다는 애매한 옵션이다.

const getSettings = (): GameSettings => {
  return {
    speed: 'fast',
    quality: 'high'
  }
};

const settings = getSettings();

settings.speed; // 의도함
settings.quality; // 의도함
// Property 'user' comes from an index signature, so it must be accessed with ['user'].ts(4111)
settings.user; // 의도한건가..?

 

speed와 quality 프로퍼티는 반드시 존재해야하는게 확실하지만 user의 경우는 의도된 경우인지 알 수 없다. 해당 옵션을 활성화하면, 존재하지 않을 가능성이 있는 프로퍼티에 대해 settings['user'] 문법으로 접근하여 이 두 케이스를 구분해 일관성있는 코딩을 작성하는 것을 강제한다.

 

해당 옵션은 eslintdot-notation 옵션과 충돌된다.

["user"] is better written in dot notation.eslint(dot-notation)

 

noUncheckedIndexedAccess

  • true: 암묵적인 undefined 반환을 금지.
  • false: (기본 값) 암묵적인 undefined 반환을 허용.

인덱스 시그니처로 선언된 프로퍼티는 존재할 수도있고, 존재하지 않을 수도 있다. 해당 옵션이 없으면, 딱 선언한 대로만 추론해준다. 따라서, env.NODE_ENV의 타입은 string이 아닌 string | undefined 가 올바르다. 해당 옵션을 켜면 string | undefined 으로 추론해서, 혹시 발생할 수 있는 런타임 레퍼런스 에러를 방지해준다.

interface EnvironmentVars {
  NAME: string;
  OS: string;
 
  // Unknown properties are covered by this index signature.
  [propName: string]: string;
}
 
declare const env: EnvironmentVars;
 
// Declared as existing
const sysName = env.NAME;
const os = env.OS;
      
const os: string
 
// Not declared, but because of the index
// signature, then it is considered a string
const nodeEnv = env.NODE_ENV;

 

false일때,

true일때,

 

noUnusedLocals

함수 내부에 사용하지 않는 지역 변수가 존재할 경우, 허용할 것인지에 대한 옵션이다. (전역 스코프나 모듈 스코프에 있는 변수에는 해당하지 않는 옵션이다.)

  • true: 사용하지 않는 로컬 변수가 있다면 에러 발생.
  • false: (기본 값) 사용하지 않는 로컬 변수가 있어도 무시.
const foo = () => {
  >> 'bar' is declared but its value is never read.ts(6133)
  const bar = 42;
};

 

noUnusedParameters

사용하지 않는 파라미터를 허용할 것인지에 대한 옵션이다.

  • true: 사용하지 않는 파라미터가 있다면 에러 발생.
  • false: (기본 값) 사용하지 않는 파라미터가 있어도 무시.
>>  'modelID' is declared but its value is never read.ts(6133)
const createDefaultKeyboard = (modelID: number) => {
  const defaultModelID = 23;
  return { type: 'keyboard', modelID: defaultModelID };
};

Strict Type-Checking Options

strict 옵션들은 타입 평가시 얼마나 엄격하게 평가할지에 대한 옵션들이다.

 

alwaysStrict

ECMAScript 엄격 모드에서 구문 분석되고 각 소스 파일에 대해 "use strict"를 내보낸다.

  • true: Strict 규칙을 위반할 경우 에러 발생.
  • false: (기본 값) Strict 규칙을 위반을 허용한다.

타입스크립트의 대부분의 Strict 모드 관련 옵션들은 "모듈이 아닌 코드"를 위한 것이다. 타입스크립트는 ECMAScript 2015의 Strict Mode Code 섹션의 정의에 따라 모든 모듈 코드는 반드시 Strict 모드로 컴파일하기 때문이다.

 

// Octal literals are not allowed in strict mode.ts(1121)
const number = 023;

 

noImplicitAny

암시적인 any에 대해서 오류를 발생시킨다.

  • true: 타입 추론이 불가능하고 타입 선언도 되지 않은 값에 대해 암묵적인 any 형변환이 일어나는 것을 금지한다.
  • false: (기본 값) 타입 추론이 불가능하고 타입 선언도 되지 않은 값에 대해 암묵적인 any 형변환이 일어나는 것을 허용한다.

Typescript는 타입 추론 불가능하고, 타입 선언이 되어있지 않을때, any 타입으로 평가한다. 이는 타입 안정성에 안좋은 영향을 준다.

>> Parameter 'num' implicitly has an 'any' type.
const anyFunction = (num) => {
  return num;
};
anyFunction(42);

 

noImplicitThis

this가 암묵적으로 any로 평가되것에 대해 허용할지에 대한 옵션이다.

  • true: this가 암묵적으로 any로 평가되것에 대해 금지
  • false: (기본 값) this가 암묵적으로 any로 평가되것에 대해 허용

Rectangle에 대한 this가 아니므로 암묵적으로 any로 평가된다.

class Rectangle {
  width: number;
  height: number;
 
  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }
 
  getAreaFunction() {
    return function () {
      return this.width * this.height;
>> 'this' implicitly has type 'any' because it does not have a type annotation.
>> 'this' implicitly has type 'any' because it does not have a type annotation.
    };
  }
}

 

strict

strict 관련 옵션들을 일괄적으로 켠다.

  • true: Strict 관련 옵션들을 일괄적으로 켠다.
  • false: (기본 값) Strict 관련 옵션들을 개별적으로 켠다.

strict 관련 옵션에 해당하는 옵션들은 아래와 같다.

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

타입스크립트 팀은 추후 배포될 타입스크립트 버전에 추가되는 새로운 Strict mode family 옵션에 대해, strict 옵션에 추가할 예정이므로 추후 타입스크립트 버전을 업데이트를 하면 새로운 타입 에러가 날 수도 있다.

 

strictBindCallApply

  • true: callapplybind를 사용하여 함수를 호출했을 때에도 인자의 타입을 검사한다.
  • false: (기본 값) call, apply, bind를 사용하여 함수를 호출했을 때에도 인자의 타입을 검사하지 않는다.

옵션이 꺼져있는 상태에서 call, apply, bind를 사용하게 되면 타입스크립트는 함수의 인자에 제대로 된 타입이 넘겨졌는지 검사하지 않기 때문에, 이로인해 런타임 타입 에러가 발생할 수도 있다. 에러가 나지 않으면, 반환 타입은 any이다.

 

function fn(x: string) {
  return parseInt(x, 10);
}

const n1 = fn.call(undefined, '10');

// Argument of type 'boolean' is not assignable to parameter of type 'string'.ts(2345)
const n2 = fn.call(undefined, false);

 

strictFunctionTypes

  • true: 함수의 인자를 반공변적인 타입으로 평가
  • false: (기본 값) 함수의 인자를 이변적인 타입으로 평가

공변과 반공변에 대한 자세한 내용은 다음 게시글을 참고하자.

 

let foo: Array<string> = [];
let bar: Array<string | number> = [];

bar = foo;
// Type '(string | number)[]' is not assignable to type 'string[]'.
//   Type 'string | number' is not assignable to type 'string'.
//     Type 'number' is not assignable to type 'string'.ts(2322)
foo = bar;

Array<string>타입인  foo 변수에 Array<string | number> 타입인 bar를 할당하지 못한다고 오류가 났다. 그 이유는 string이 string | number보다 작은 개념이기 때문이다.

 

string | number를 string의 슈퍼 타입이라고 한다. (부분 집합: subset, 상위 집합: superset 을 보면 super type이라 부르는게 이해된다.)

 

어떤 타입 T 가 타입 T'의 서브 타입이며, C<T>가 C<T'>의 서브타입일때, 타입 C를 공변성을 가지고 있다고 한다.

 

그의 반대는 반공변성을 가지고 있다고한다.

 

위의 예시에서, stringstring | number의 서브타입이지만, Array<string>Array<string | number>를 할당할 수 없으므로 Array<string>Array<string | number>의 서브타입이 아니다.

 

따라서 위 예시는 반공변적이고, 반공변적으로 평가되는 것이 더 안전하기에 옵션을 제공한다. 참고로 이변적은 반공변성과 공변성 모두 포함하는 개념이다.

 

strictNullChecks

  • true: 구체적인 값이 존재해야하는 상황에서, false, undefined, null일 가능성이 있으면 에러 발생
  • false: (기본 값) 구체적인 값이 존재해야하는 상황에서, false, undefined, null일 가능성이 있어도 무시

구체적인 값이 존재해야하는 상황은 아래와 같이 loggedInUser.age 처럼 어떤 객체의 프로퍼티에 접근할 때 발생한다.

declare const loggedInUsername: string;

const users = [
  { name: 'Oby', age: 12 },
  { name: 'Heera', age: 32 },
];

const loggedInUser = users.find((u) => u.name === loggedInUsername);
// Object is possibly 'undefined'.ts(2532)
console.log(loggedInUser.age);

 

Array.prototype.find 메소드는 T | undefined 타입을 반환하도록 정의되어있다. 따라서, loggedInUser는 undefined가 될 수 있으므로 런타임 참조 에러가 발생할 가능성이 있다.

 

따라서, 이런 상황에서는 명시적으로 if문을 사용해서 loggedInUser가 null이나 undefined인지 확인하거나, 옵셔널 체이닝 문법 (loggedInUser?.age)를 사용해서 해결 할 수 있다.

 

strictPropertyInitialization

  • true: 클래스의 프로퍼티가 선언되었지만, constructor에 초기화가 되지 않았을 때 에러 발생
  • false: (기본 값) 클래스의 프로퍼티가 선언되었지만, constructor에 초기화가 되지 않아도 무시

아래 예시에서 email은 string이여야하지만, email에 undefined가 할당되어서 객체가 생성된다. 즉, 타입 안정성이 깨지게된다. 따라서 해당 옵션이 생겨났다.

class UserAccount {
  name: string;
  accountType = 'user';

  //   Property 'email' has no initializer and is not definitely assigned in the constructor.ts(2564)
  email: string;
  address: string | undefined;

  constructor(name: string) {
    this.name = name;
    // Note that this.email is not set
  }
}

 

useUnknownInCatchVariables (typescript v4.4 부터 사용 가능하다.)

  • true: catch 문의 error 인자를 unknown 타입으로 평가.
  • false: (기본 값) catch 문의 error 인자를 any 타입으로 평가.

타입스크립트는 catch문의 error에대해 any 타입으로 평가하기 때문에 아래 예시에서 err.message는 런타임 참조 에러가 날 수 있다. 따라서 해당 옵션이 추가되었다. any의 경우는 무시를 하지만, unknown일 경우는 타입 정의를 해주어야한다. 

try {
  // ...
} catch (err) {
  // We have to verify err is an
  // error before using it as one.
  if (err instanceof Error) {
    console.log(err.message);
  }
}

Module Resolution Options

allowUmdGlobalAccess

728x90

'Tech > Typescript' 카테고리의 다른 글

Partial migration of javascript to typescript  (0) 2021.10.07
Typescript basic template 만들기  (0) 2021.08.12

댓글