✍️ Type guard (타입 보호): 정확한 의미 전달을 위해 "type guard"는 한글로 번역하지 않았습니다.
Type Guard
Type guard를 사용하면 조건문에서 객체의 타입을 좁혀나갈 수 있습니다.
typeof
TypeScript는 JavaScript의 instanceof, typeof 연산자를 이해할 수 있습니다. 즉 조건문에 typeof와 instanceof를 사용하면, TypeScript는 해당 조건문 블록 내에서는 해당 변수의 타입이 다르다는 것(=좁혀진 범위의 타입)을 이해한다는 것이죠. 아래 예시를 보시면 TypeScript는 특정 메소드(String.prototype.substr)가 string에 존재하지 않는다는 사실을 인식해 사용자 오타가 있을 수 있음을 지적하고 있습니다.
functiondoSomething(x:number|string) {if (typeof x ==='string') { // TypeScript는 이 조건문 블록 안에 있는 `x`는 백퍼 `string`이란 걸 알고 있습니다.console.log(x.subtr(1)); // Error: `subtr`은 `string`에 존재하지 않는 메소드입니다.console.log(x.substr(1)); // ㅇㅋ }x.substr(1); // Error: `x`가 `string`이라는 보장이 없죠.}
TypeScript는 a == null / != null로 null과 undefined 모두 걸러낼 만큼 똑똑한 친구입니다. 가령:
functionfoo(a?:number|null) {if (a ==null) return;// 이제부터 a는 무조건 number입니다.}
사용자 정의 Type Guards
JavaScript 언어는 풍부한 런타임 내부 검사(=runtime introspection support)를 지원하진 않습니다. 일반 JavaScript 객체(구조적 타입 structural typings 활용)를 사용할 때에는 instanceof나 typeof와 같은 연산자를 액세스 조차 할 수 없습니다. 하지만 TypeScript에서는 사용자 정의 Type Guard 함수를 만들어 이를 해결할 수 있습니다. 사용자 정의 Type Guard 함수란 단순히 어떤 인자명은 어떠한 타입이다라는 값을 리턴하는 함수일 뿐입니다. 아래에 예시를 보겠습니다:
/** * 일반적인 인터페이스 예 */interfaceFoo { foo:number; common:string;}interfaceBar { bar:number; common:string;}/** * 사용자 정의 Type Guard! */functionisFoo(arg:any): arg isFoo {returnarg.foo !==undefined;}/** * 사용자 정의 Type Guard 사용 예시 */functiondoStuff(arg:Foo|Bar) {if (isFoo(arg)) {console.log(arg.foo); // ㅇㅋconsole.log(arg.bar); // Error! }else {console.log(arg.foo); // Error!console.log(arg.bar); // ㅇㅋ }}doStuff({ foo:123, common:'123' });doStuff({ bar:123, common:'123' });
Type Guard와 Callback
TypeScript는 콜백 함수 내에서 type guard가 계속 유효하다고 여기지 않습니다. 이는 매우 위험하기 때문입니다. 가령:
// Example Setupdeclarevar foo:{bar?: {baz:string}};functionimmediate(callback: () =>void) {callback();}// Type Guardif (foo.bar) {console.log(foo.bar.baz); // ㅇㅋfunctionDoingSomeStuff(() => {console.log(foo.bar.baz); // TS error: 해당 객체는 'undefined'일 가능성이 있습니다. });}
해결법은 아주 간단합니다. 로컬 변수를 선언하고 그 안에 값을 담아 타입 추론이 가능하도록 만드는 것이죠. 이는 해당 변수의 타입이 외부 요인으로 인해 바뀔 가능성이 없다는 걸 자동으로 보장하고, TypeScript 또한 이를 쉽게 이해할 수 있습니다: