strict 모드 깊게 — 7가지 옵션 + 보너스
strict 가족이 켜는 검사들과 추가로 켤 값진 옵션 3개.
strict: true 한 줄이 7가지 검사를 동시에 켭니다. 각각이 어떤 버그를 잡는지 알면 어떤 strict 옵션을 켤지·끌지 판단하기 쉽습니다. 18편은 strict 7가족을 하나씩 풀고, 그 위에 추가로 켜면 좋은 옵션 3개까지.
strict — 7가지 옵션을 한 번에
{
"compilerOptions": {
"strict": true
// = strictNullChecks
// + noImplicitAny
// + strictFunctionTypes
// + strictBindCallApply
// + strictPropertyInitialization
// + noImplicitThis
// + useUnknownInCatchVariables
// + alwaysStrict
}
}
① strictNullChecks — 가장 중요
// 끄면 — null/undefined 가 어디든 들어감 (위험)
function len(s: string) { return s.length; }
len(null); // 컴파일 통과 → 런타임 에러
// 켜면 — null/undefined 는 명시적으로
function len(s: string | null) {
if (s === null) return 0;
return s.length;
}
strict 가족 중 가장 큰 가치. "이 변수가 null 일 수 있나" 가 타입에 보입니다.
② noImplicitAny — 타입 적게 빠뜨리기
// 끄면
function add(a, b) { return a + b; } // a, b 는 any
add("1", 2); // 통과
// 켜면 — 매개변수에 타입 강제
function add(a: number, b: number) { ... } // 안 적으면 에러
③ strictFunctionTypes — 함수 매개변수 변성
// Animal 받는 함수에 Dog 받는 함수 할당?
// 끄면 통과, 켜면 막힘 (안전)
type AnimalHandler = (a: Animal) => void;
type DogHandler = (d: Dog) => void;
let h: AnimalHandler = (d: Dog) => { ... };
// 끄면 OK — 하지만 h(cat) 했을 때 d.bark() 호출하면 런타임 에러
// 켜면 에러로 차단
④ strictPropertyInitialization — 클래스 필드 초기화
// 끄면
class User {
name: string; // 초기값 없음 → 사용 시 undefined
}
// 켜면
class User {
name: string; // ❌ 'name' has no initializer
// 해결 1: 초기값
name: string = "";
// 해결 2: 생성자에서
constructor(name: string) { this.name = name; }
// 해결 3: ! (확실히 나중에 할당함을 약속)
name!: string;
}
⑤ noImplicitThis — this 의 타입
// 끄면 this 가 any
function fn() {
console.log(this.x); // any
}
// 켜면 — 일반 함수의 this 는 사용 전 명시 필요
function fn(this: { x: number }) {
console.log(this.x); // OK
}
⑥ useUnknownInCatchVariables — catch 의 안전
try { ... } catch (err) {
// 끄면 err: any (위험)
// 켜면 err: unknown — 검사 후 사용
if (err instanceof Error) console.error(err.message);
}
⑦ strictBindCallApply — bind/call/apply 의 타입 검사
function greet(name: string) { return `Hi, ${name}`; }
greet.call(null, 42); // 끄면 통과, 켜면 에러 (number !== string)
보너스 1 — noUncheckedIndexedAccess
"noUncheckedIndexedAccess": true
const arr = [1, 2, 3];
const x = arr[10]; // 끄면 x: number (거짓말), 켜면 x: number | undefined
if (x !== undefined) {
x.toFixed(2); // OK
}
// Record 인덱스도
const map: Record<string, number> = { a: 1 };
const v = map["b"]; // 켜면 v: number | undefined
가장 중요한 보너스. strict 만으로는 배열·객체 인덱스 접근이 항상 값이 있는 것처럼 다룸. 이 옵션이 그 거짓말을 막아줍니다. 약간 짜증나지만 진짜 버그 잡습니다.
보너스 2 — exactOptionalPropertyTypes
"exactOptionalPropertyTypes": true
interface Config {
port?: number;
}
// 끄면
const c: Config = { port: undefined }; // OK
// 켜면
const c: Config = { port: undefined };
// ❌ Type 'undefined' is not assignable to type 'number'
// → 명시적으로 undefined 보내려면 port?: number | undefined 로 정의해야
"?"(있음/없음) 와 "undefined"(값으로 undefined) 의 구분. JSON·API 응답 다룰 때 의미 있는 차이.
보너스 3 — noImplicitOverride
"noImplicitOverride": true
class Cat extends Animal {
speak() { ... } // ❌ Must use 'override' modifier
override speak() { ... } // ✅
}
부모 메서드 오버라이드할 때 명시. 부모 메서드 이름이 바뀌면 자식에서 컴파일 에러로 즉시 알림.
실전 단계별 도입
| 단계 | 설정 | 대상 |
|---|---|---|
| 0 | strict: false | 옛 JS 코드 마이그레이션 시작 |
| 1 | noImplicitAny: true | 매개변수 타입 채우기 |
| 2 | strictNullChecks: true | null 처리 정리 |
| 3 | strict: true | 새 코드의 표준 |
| 4 | + noUncheckedIndexedAccess | 진짜 안전 (배열/Record) |
| 5 | + exactOptionalPropertyTypes | 미세 안전 (선택) |
| 6 | + noImplicitOverride | 상속 안전 |
점진 도입 가이드. 기존 코드 베이스가 크면 한 번에 strict 켜지 말고 위 단계대로. 각 단계마다 발생한 에러를 정리한 다음 다음 옵션. strict-only 디렉터리부터 시작해 단계적으로 확장도 OK.
19편 — 빌드 성능 (incremental·project references)
모노레포·큰 프로젝트의 빌드 시간 줄이기.
📚 쉽게 배우는 타입스크립트 교재
이전: 17편 .d.ts · 현재: 18편 (고급) · 다음 → 19편 빌드 성능 · 진행: 18/20
이전: 17편 .d.ts · 현재: 18편 (고급) · 다음 → 19편 빌드 성능 · 진행: 18/20