Narrowing vs Assertion (feat.모르고 쓰면 머리깡 당함)

만분의 일·2022년 8월 22일
0

TypeScript

목록 보기
5/7

Narrowing

아래처럼 파라미터의 type이 number인지 string인지 명확히 정해지지 않았기 때문에 error발생한다.

typescript에서는 Union Type을 바로 조작할 수 없다.
따라서 Type이 아직 하나로 확정되지 않았을 경우 Type Narrowing을 써야 한다.

function name (x:number | string){
	return x+1;
}
name(123);

안정성을 중요하게 여기는 TypeScript에서는 x의 타입을 좁혀준 후에 조작 할 수 있다. 이를 Narrowing이라고 한다.

narrowing 방법

typeof type guards

  • 어떤 변수의 타입이 불확실한 경우 typeof키워드와 if문을 이용해 변수의 타입을 직접 체크하는 것으로 Narrowing을 할 수 있다.

🚨주의: if문 썼으면 끝까지 써야 안전하다. else, else if 안쓰면 에러 발생할지도,,,

function name (x :number|string){
	if (typeof x === 'string'){
		return x + '1'
		} else {
			return x + 1
	}
}

name(123);

Equality narrowing

  • x === y가 참인 경우는 x와 y가 모두 number 타입일 경우뿐이므로 Narrowing 할 수 있다.
function name(x: string | number, y: boolean | number) {
  if (x === y) {
    return x + y;
  }
}
export {};

The in operator narrowing

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ("move" in animal) {
    return animal.swim();
  }

  return animal.fly();
}

animal이 swim속성을 가지고 있다면 Fish타입, 그렇지 않다면 Bird 타입임을 알 수 있다.

type Fish = { move: () => void };
type Bird = { move: () => void };

function move(animal: Fish | Bird) {
  if ('move' in animal) {
    return animal.move();
  }

  return animal.move();
}

이 경우에는 FishBird 모두 move를 갖고 있기 때문에, 'move' in animal로 타입을 좁힐 수 없어 에러가 발생한다.


instanceof narrowing

function logValue(x: Date | string) {
  if (x instanceof Date) {
    console.log(x.toUTCString());
  }
}

prototype을 체크해주는 instanceof 키워드로도 Narrowing을 할 수 있다.

toUTCString: UTC기준의 시간으로 시간을 변형해서 문자열로 리턴해준다.
instanceof: 생성자의 prototype속성이 객체의 어느 프로토타입에 존재하는지 판별한다.

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);

console.log(auto instanceof Car);
// expected output: true

console.log(auto instanceof Object);
// expected output: true

===================

function name(x: number | string) {
  let array: number[] = [];
  array[0] = x;        // Type 'string | number' is not assignable to type 'number'.
													Type 'string' is not assignable to type (x의 type이 명확히 안정해짐)
}

export {};

// 따라서 if문으로 type확정 해줘야 한다.

function name(x: number | string) {
	  let array: number[] = [];
		if(typeof x === 'number'){ // number이라고하면 값을, 'number'이라고 하면 유형을 나타낸다.(typeof로 비교할 땐 글자로 즉'number'로 비교해야함
			array[0] = x;
		}
  
}

export {};

Assertion

narrowing 대신 type을 확정 시킬 때 쓰는 assertion문법이 있다.(타입 덮어씌우기)

개발자가 해당 타입에 대해 확신이 있을 때 사용하는 타입 지정 방식이다.

다른 언어의 타입 캐스팅과 비슷한 개념이며, 타입스크립트에서 특별히 타입을 체크하지 않고, 데이터의 구조도 신경쓰지 않는다.

타입 캐스팅
로그래밍 언어에서, 어떤 형의 데이터를 다른 형으로 변환하는 것.
예를 들면, 정수형인 ‘1’을 실수형인 ‘1.0’으로 변환하는 것을 가리킨다.

🚨주의! Assertion은 type을 확정시키는것이지 a에서 b로 변경 시킬 수는 없다.🙅‍♀️

function name(x: number | string) {
   
	let array: number[] = [];
  array[0] = x;
}

export {};

// 위의 코드에 Aseertion 문법을 적용해보자.

function name(x: number | string) {
   
	let array: number[] = [];
  array[0] = x as number; // "왼쪽에 있는 변수를 number로 덮어씌워주세요" 라는 의미
}

	let name1:string = 'kim';
	name1 as number; // error. a에서 b로 **변경** 시킬 수 없다

export {};


Assertion이 narrow보다 사용하기에 간편하다.

그렇다고 💁‍♀️ “와 Aseertion 겁나 편하네? 앞으로 자주 써야 겠다” 며 Assertion을 주구장창 쓰면 사수한테 머리를 깡하고 맞을 수도 있다.

컴퓨터 문법은 필요 없이 만들어진게 없다. 비슷해보여도 다 의도가 다르다.

비슷해 보이지만 왜 굳이 또 다른 문법이 만들어졌는지 주의해야한다.



그래서 Assertion의 용도가 뭔데?🤷‍♀️

첫번째, 복잡한 union type을 하나의 type으로 확정하고 싶을 때 사용한다.
(여기까진 narrowing과 똑같음)

두번째, 파라미터 값에 어떤 타입이 들어올지 100% 확실할 때 사용해야한다.

as를 이용해 number 타입으로 확정시키면 number 타입으로 “덮어씌운것”이기 때문에, 아래와 같이 문자를 넣어도 error가 발생하지 않는다.

function name(x: number | string) {
   
	let array: number[] = [];
  array[0] = x as number; 
}
name('123') // 문자를 넣어도 error발생하지 않는다.

따라서 Assertion은 어떤 타입이 들어올지 100% 확실할 때 사용한다는 것 말고는 다른 것이 없어서 굳이 쓸 이유가 없다. 괜히 error를 못잡아내는 일만 생길 수 있기 때문! 따라서 왠만하면 Arrow 문법을 사용하는 것이 낫다.

(assertion은 남이 짠 코드 수정할 때, 왜 타입 에러가 나는지 모를 때 비상용으로 쓰는 것을 추천한다)

profile
1/10000이 1이 될 때 까지

0개의 댓글