본문으로 건너뛰기
김신건의 로그

[TypeScript] Strict Mode & tsconfig

· 수정 · 📖 약 3분 · 961자/단어 #typescript #tsconfig #strict #compiler
TypeScript Strict Mode, TS strict, strictNullChecks, noImplicitAny, strictFunctionTypes, noUncheckedIndexedAccess, TypeScript tsconfig, TypeScript compiler options, TS 엄격 모드

정의

Strict Mode 는 여러 엄격한 타입 검사 플래그를 한 번에 켜는 tsconfig.json 옵션 (strict: true) 입니다. 강력한 타입 안전성을 제공하지만, 기존 코드베이스에는 대량의 오류를 발생시킬 수 있어 도입 시 계획이 필요합니다.

strict: true 로 켜지는 것

{
  "compilerOptions": {
    "strict": true
  }
}

이는 다음을 모두 활성화합니다:

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

각각을 개별로 켜기도 가능.

noImplicitAny

암묵적 any 를 금지.

function log(msg) {            // 오류: msg 의 타입이 any
  console.log(msg);
}

function log(msg: string) {    // OK
  console.log(msg);
}

함정: 마이그레이션 초기에는 많은 오류. // @ts-nocheck 나 파일 단위 any 로 임시 완화.

strictNullChecks

nullundefined 를 값 있는 타입에 포함 안 함.

let name: string = null;       // 오류
let name: string | null = null;   // OK

function greet(name: string) { ... }
greet(null);                    // 오류
greet(undefined);               // 오류

Array.prototype.find 등이 자동으로 T | undefined 리턴:

const users = [{id: 1}, {id: 2}];
const found = users.find(u => u.id === 3);
found.id;   // 오류: found 는 undefined 일 수 있음

if (found) {
  found.id;   // OK
}

가장 중요한 옵션. 반드시 켜세요.

strictFunctionTypes

함수 매개변수 타입을 contravariant 로 검사 (더 엄격).

type Animal = {name: string};
type Dog = {name: string; bark(): void};

let f: (a: Animal) => void = (d: Dog) => d.bark();   // 오류 (strictFunctionTypes)

이 옵션 없이는 bivariant (덜 엄격) 로 허용됨. 안전성을 위해 켜세요.

예외: method 시그니처 (interface X { method(a: A): void }) 는 여전히 bivariant. 함수 필드 (interface X { method: (a: A) => void }) 만 strict.

strictBindCallApply

fn.call, fn.apply, fn.bind 를 엄격 검사.

function greet(name: string, age: number) { ... }

greet.call(null, "kim", 30);    // OK
greet.call(null, "kim");         // 오류: age 누락
greet.call(null, "kim", "thirty");   // 오류: age 타입 불일치

strictPropertyInitialization

Class field 가 constructor 에서 반드시 초기화.

class User {
  name: string;                  // 오류: constructor 에서 초기화 안 함
  email: string = "";             // OK: 기본값
  age!: number;                   // OK: definite assignment (외부에서 초기화 보장)

  constructor(name: string) {
    this.name = name;             // OK: constructor 에서
  }
}

noImplicitThis

this 의 타입이 암묵적 any 인 경우 오류.

function bad() {
  console.log(this.x);   // 오류: this 가 any
}

function good(this: {x: number}) {
  console.log(this.x);   // OK
}

useUnknownInCatchVariables

catch 절의 예외를 any 대신 unknown 으로.

try {
  ...
} catch (e) {
  e.message;              // 오류 (useUnknownInCatchVariables)
  if (e instanceof Error) {
    e.message;            // OK
  }
}

TS 4.4+ 에 도입. strict: true 로 자동 활성.

alwaysStrict

Emit 되는 JS 에 "use strict" 자동 삽입 + parse 시 strict mode.

Strict 외 자주 켜는 옵션

noUncheckedIndexedAccess

배열/객체 인덱스 접근이 T | undefined 를 리턴.

const arr: number[] = [1, 2, 3];
const x = arr[0];   // strict 만: number
                     // + noUncheckedIndexedAccess: number | undefined
const map: Record<string, string> = {};
const y = map["nokey"];   // + noUncheckedIndexedAccess: string | undefined

실전에서는 배열 접근이 자주 undefined 를 반환하므로 켜는 편이 안전. 하지만 for loop 등 이미 안전한 접근도 오류.

noImplicitReturns

함수의 모든 경로가 값을 리턴해야 함.

function example(x: boolean): string {
  if (x) return "yes";
  // 오류: else 경로가 return 없음
}

noFallthroughCasesInSwitch

Switch case 의 fall-through 를 오류.

switch (x) {
  case 1:
    doA();
    // 오류: break/return 없이 다음 case 로 떨어짐
  case 2:
    doB();
    break;
}

noUnusedLocals, noUnusedParameters

미사용 변수/파라미터 오류.

function f(a: number, b: number) {   // 오류: b 미사용
  return a * 2;
}

ESLint 로 처리하는 편이 관용 (더 유연).

exactOptionalPropertyTypes

x?: Tundefined 명시적 할당 금지.

type Opts = {debug?: boolean};

const o: Opts = {debug: undefined};   // 오류 (exactOptionalPropertyTypes)

미묘하지만 실전 케이스가 있어 켜는 프로젝트도 있음.

noPropertyAccessFromIndexSignature

Index signature 필드는 .foo 대신 ["foo"] 만 허용:

const obj: {[key: string]: string} = {a: "1"};

obj.a;         // 오류 (noPropertyAccessFromIndexSignature)
obj["a"];      // OK

명시적 필드와 index signature 를 구분하여 오타 방지.

권장 (2026 표준)

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "esnext",
    "moduleResolution": "bundler",
    "lib": ["ES2022", "DOM"],
    "jsx": "react-jsx",

    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true,

    "esModuleInterop": true,
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,

    "noEmit": true
  },
  "include": ["src/**/*"]
}

skipLibCheck: true 는 node_modules 의 .d.ts 검사 스킵 (성능). 대부분의 프로젝트에서 켜는 편.

Strict 도입 전략

1. 신규 프로젝트

처음부터 strict: true. 코드가 자연스럽게 strict 를 준수.

2. 기존 프로젝트

점진 도입:

Step 1: noImplicitAny: false 로 시작, 다른 strict 옵션 하나씩:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": false
  }
}

Step 2: 파일별 // @ts-strict-ignore 로 opt-out (커스텀 도구 필요) 또는 // @ts-nocheck 로 개별 무시.

Step 3: 도구 (ts-migrate, type-coverage) 로 커버리지 측정, 점진 개선.

Step 4: 전 코드가 strict 준수하면 noImplicitAny: true 로.

3. tsc-baseline

에러를 기록해두고 새 오류만 실패시키는 도구. git bisect 스타일로 점진 개선.

성능 최적화

  • skipLibCheck: true: node_modules .d.ts 검사 스킵
  • incremental: true: .tsbuildinfo 캐시로 재컴파일 가속
  • composite: true: project reference 활성화 (모노레포)
  • --extendedDiagnostics: 컴파일 시간 프로파일

함정

WARNING

skipLibCheck: false 는 방대한 시간 소비. @types/* 안의 오류까지 검사. 특별한 이유 없으면 true.

CAUTION

strictPropertyInitialization + Reflect / DI. NestJS, TypeORM 등이 constructor 밖에서 필드 초기화하면 오류. ! 로 assert.

WARNING

noUncheckedIndexedAccess 는 광범위 영향. 모든 배열 인덱스 접근이 T | undefined. for-of 나 map 로 안전 접근하는 편이 관용.

IMPORTANT

strict 를 부분 활성화하면 명시. strict: true + 개별 false override 는 리팩토링 시 잊기 쉬움. 명시적 리스트가 유지보수 유리.

CAUTION

Emit 대상과 lib 불일치. target: "ES2022" + lib: ["ES2015"] 은 이상. lib 은 최소 target 이상.

관련 위키

이 글의 용어 (7개)
[TypeScript] Declaration Files (.d.ts)typescript
정의 Declaration File ( ) 은 타입 정보만 담은 파일입니다. 실행 코드 없이 함수, 클래스, 변수, 모듈의 타입 시그니처 만 선언합니다. JavaScript 라이…
[TypeScript] Decoratorstypescript
정의 Decorator 는 class, method, accessor, property, parameter 에 부착되는 함수로 그 대상을 검사/수정할 수 있습니다. TypeScr…
[TypeScript] Genericstypescript
정의 Generics 는 타입을 파라미터화하는 문법입니다. 함수, 클래스, 인터페이스, type alias 가 재사용 가능 하면서도 타입 안전 하도록 만듭니다. Java 의 ge…
[TypeScript] Modules (import/export, moduleResolution)typescript
정의 TypeScript 모듈 시스템은 ES Modules (ESM) 를 기본 언어 모델로 삼되, CommonJS (CJS) 를 상호운용성 (interop) 계층으로 지원합니다.…
[TypeScript] Primitive Typestypescript
정의 Primitive Types 는 TypeScript 의 가장 기본 타입 계열입니다. JavaScript 의 7가지 primitive 값 + 특수 타입 (void, never…
[TypeScript] Type Narrowing (Type Guards)typescript
정의 Type Narrowing 은 TypeScript 컴파일러가 제어 흐름 분석 (control flow analysis) 을 통해 특정 위치에서 값의 타입을 더 좁게 추론하는…
TypeScripttypescript
정의 TypeScript 는 Microsoft 가 2012년 발표한 JavaScript 의 상위 집합 프로그래밍 언어입니다. 정적 타입 시스템, 인터페이스, 제네릭, enum, …

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기