Type Guard

์‹ ํƒœ์ผยท2024๋…„ 10์›” 16์ผ

๐Ÿ’ก Type guard (ํƒ€์ž… ๋ณดํ˜ธ): ์ •ํ™•ํ•œ ์˜๋ฏธ ์ „๋‹ฌ์„ ์œ„ํ•ด โ€œtype guardโ€๋Š” ํ•œ๊ธ€๋กœ ๋ฒˆ์—ญํ•˜์ง€ ์•Š๋Š”๋‹ค.

Type guard๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์กฐ๊ฑด๋ฌธ์—์„œ ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ์ขํ˜€๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.

typeof

TypeScript๋Š” JavaScript์˜ instaceof, typeof ์—ฐ์‚ฐ์ž๋ฅผ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰ ์กฐ๊ฑด๋ฌธ์— typeof์™€ instanceof๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, TypeScript๋Š” ํ•ด๋‹น ์กฐ๊ฑด๋ฌธ ๋ธ”๋ก ๋‚ด์—์„œ๋Š” ํ•ด๋‹น ๋ณ€์ˆ˜์˜ ํƒ€์ž…์ด ๋‹ค๋ฅด๋‹ค๋Š” ๊ฒƒ(=์ขํ˜€์ง„ ๋ฒ”์œ„์˜ ํƒ€์ž…)์„ ์ดํ•ดํ•œ๋‹ค. ์•„๋ž˜ ์˜ˆ์‹œ๋ฅผ ๋ณด๋ฉด TypeScript๋Š” ํŠน์ • ๋ฉ”์†Œ๋“œ(String.prototype.substr)๊ฐ€ string์— ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ธ์‹ํ•ด ์‚ฌ์šฉ์ž ์˜คํƒ€๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Œ์„ ์ง€์ ํ•˜๊ณ  ์žˆ๋‹ค.

function doSomething(x: number | string) {
	if (typeof x === 'string') {
		console.log(x.subtr(1)); // TypeScript๋Š” 'subtr'์€ 'string'์— ์กด์žฌํ•˜์ง€ ์•Š์Œ์„ ์•Œ๊ณ  ๊ฒฝ๊ณ ๋ฅผ ๋‚ด๋ณด๋‚ธ๋‹ค
		console.log(x.substr(1));
	}
	x.substr(1); // Error -> x๊ฐ€ 'string'์ด๋ผ๋Š” ๋ณด์žฅ์ด ์—†๋‹ค.
}

instanceof

class Foo {
	foo = 123;
	common = '123';
}

class Bar {
	bar = 123;
	common = '123';
}

function doStuff(arg: Foo | Bar) {
	if (arg instanceof Foo) {
		console.log(arg.foo); // 
		console.log(arg.bar); // Error! -> Foo์—๋Š” bar๊ฐ€ ์—†๋‹ค.
	}
	
	 if (arg instanceof Bar) {
    console.log(arg.foo); // Error!
    console.log(arg.bar); // ใ…‡ใ…‹
  }

  console.log(arg.common); // ใ…‡ใ…‹
  console.log(arg.foo); // Error!
  console.log(arg.bar); // Error!
 }
 
 doStuff(new Foo());
 doStuff(new Bar());

TypeScript๋Š” else ๋˜ํ•œ ์ดํ•ดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๊ฐ€ if๋ฌธ์œผ๋กœ ํƒ€์ž…์„ ํ•˜๋‚˜ ์ขํ˜€๋‚ด๋ฉด, else๋ฌธ ์•ˆ์˜ ๋ณ€์ˆ˜ ํƒ€์ž…์€ ์ ˆ๋Œ€ ๋™์ผํ•œ ํƒ€์ž…์ด ๋  ์ˆ˜๋Š” ์—†์Œ ์„ ์ธ์ง€ํ•œ๋‹ค. ์•„๋ž˜ ์˜ˆ์‹œ๋ฅผ ๋ณด์ž.

class Foo {
  foo = 123;
}

class Bar {
  bar = 123;
}

function doStuff(arg: Foo | Bar) {
	if (arg instanceof Foo) {
		console.log(arg.foo); // ใ…‡ใ…‹
		console.log(arg.bar); // Error!
	} else { // if์—์„œ ์ด๋ฏธ Foo์ผ๋•Œ๋ฅผ ๊ฑธ๋ €์œผ๋‹ˆ Bar์ด๊ฒ ๊ตฐ
		console.log(arg.foo); // Error!
		console.log(arg.bar); // ใ…‡ใ…‹
	}
}

doStuff(new Foo());
doStuff(new Bar());

in

in์€ ๊ฐ์ฒด ๋‚ด๋ถ€์— ํŠน์ • property๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๋Š” ์—ฐ์‚ฐ์ž๋กœ type guard๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

interface A {
	x: number;
}

interface B {
	y: string;
}

function doStuff(q: A | B) {
	if ('x' in q) {
	// q: A
	} else {
	// q: B
	}
}

๋ฆฌํ„ฐ๋Ÿด Type Guard

๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์˜ ๊ฒฝ์šฐ === / == / !== / != ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•ด ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

type TriState = 'yes' | 'no' | 'unknown';

function logOutState(state: TriState) {
	if (state == 'yes') {
		console.log('์‚ฌ์šฉ์ž๊ฐ€ yes๋ฅผ ๊ณจ๋ž์Šต๋‹ˆ๋‹ค');
	} else if (state == 'no') {
		console.log('์‚ฌ์šฉ์ž๊ฐ€ no๋ฅผ ๊ณจ๋ž์Šต๋‹ˆ๋‹ค');
	} else {
		console.log('์‚ฌ์šฉ์ž๊ฐ€ ์•„์ง ๊ฒฐ์ •์„ ๋‚ด๋ฆฌ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค');
	}
}

์ด๋Š” union ํƒ€์ž…์— ๋ฆฌํ„ฐ๋Ÿด์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ ๋™์ผํ•˜๊ฒŒ ์ ์šฉ๋œ๋‹ค. union ํƒ€์ž…์˜ ๊ณตํ†ต property ๊ฐ’์„ ๋น„๊ตํ•ด union ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๋‹ค.

type Foo = {
	kind: 'foo', // ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…
	foo: number
}

type Bar = {
	kind: 'bar', // ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…
	bar: number
}

function doStuff(arg: Foo | Bar) { // Foo, Bar ๊ณตํ†ต ํƒ€์ž…์ด kind์ด๋‹ˆ๊นŒ ์ด๊ฑธ๋กœ ๋น„๊ต
	if (arg.kind === 'foo') {
		console.log(arg.foo); // okay
		console.log(arg.bar); // Error!
	} else {
		console.log(arg.foo); // Error!
		console.log(arg.bar); // okay
	}
}

null๊ณผ undefined (strictNullChecks)

TypeScript๋Š” a == null / != null๋กœ null๊ณผ undefined ๋ชจ๋‘ ๊ฑธ๋Ÿฌ๋‚ผ ๋งŒํผ ์—„๊ฒฉํ•˜๋‹ค.

function foo(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 ํ•จ์ˆ˜๋ž€ ๋‹จ์ˆœํžˆ ์–ด๋–ค ์ธ์ž๋ช…์€ ์–ด๋– ํ•œ ํƒ€์ž…์ด๋‹ค ๋ผ๋Š” ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜์ผ ๋ฟ์ด๋‹ค.

/**
 * ์ผ๋ฐ˜์ ์ธ ์ธํ„ฐํŽ˜์ด์Šค ์˜ˆ์‹œ
 */
 interface Foo {
	 foo: number;
	 common: string;
  }
	
 interface Bar {
	 bar: number;
	 common: string;
	}
	
/**
	* ์‚ฌ์šฉ์ž ์ •์˜ Type Guard!
	*/
	function isFoo(arg: any): arg is Foo {
		return arg.foo !== undefined;
	}
	
/**
	* ์‚ฌ์šฉ์ž ์ •์˜ Type Guard ์‚ฌ์šฉ ์˜ˆ์‹œ
	*/	
	function doStuff(arg: Foo | Bar) {
		if (isFoo(arg)) {
			console.log(arg.foo); // okay
			console.log(arg.bar); // Error!
		} else {
			console.log(arg.foo); // Error!
			console.log(arg.bar); // okay
		}
	}
	
	doStuff({ foo: 123, common: '123' });
	doStuff({ bar: 123, common: '123' });
		

ํƒ€์ž… ๊ฐ€๋“œ | TypeScript Deep Dive

profile
๋…ธ์›๊ฑฐ์ธ

0๊ฐœ์˜ ๋Œ“๊ธ€