728x90

TypeScript 대표이미지

 

728x90

 

function 내함수(x :number | string){
   return x + 1  //에러남 
}

 

타입스크립트에서 함수를 쓰다보면 위와 같은 코드에서 "Operator '+' cannot be applied to types 'string | number' and 'number' 와 같은 에러를 볼 수 있다. 이 에러를 해결하기 위해서는 타입을 하나로 Narrowing을 하거나 Assert를 하면된다.

 

🖥️ Narrowing 문법

타입스크립트에서 "Narrowing"은 특정 타입을 구체적인 타입으로 좁히는 것을 의미한다. 이는 주로 조건문과 같은 특정 상황에서 변수의 타입을 더 정확하게 추론하거나 확신하는데 사용된다. Narrowing은 주로 타입가드라고 불리는 패턴을 사용한다.

 

1. typeof를 이요한 Narrowing

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

 

위 코드를 설명하자면 x가 string일 경우와 number일 경우 또 둘다 아닐 경우로 짜야 정상적으로 사용이 가능하다. 즉, 타입이 확실하지 않을 때 생기는 부작용을 막기위한 장치라고 생각하면 된다.

 

2. instanceof를 이용한 Narrowing

class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
}

class Bird extends Animal {
    fly() {
        console.log(`${this.name} is flying.`);
    }
}

function handleAnimal(animal: Animal) {
    if (animal instanceof Bird) {
        // 여기서 animal은 Bird로 좁혀짐
        animal.fly();
    } else {
        // 여기서 animal은 Animal로 좁혀짐
        console.log(`${animal.name} can't fly.`);
    }
}

 

3. 사용자  정의 타입 가드

interface Car {
    brand: string;
    speed: number;
}

interface Bicycle {
    brand: string;
    type: string;
}

function isCar(vehicle: Car | Bicycle): vehicle is Car {
    return 'speed' in vehicle;
}

function displayVehicleInfo(vehicle: Car | Bicycle) {
    if (isCar(vehicle)) {
        // 여기서 vehicle은 Car로 좁혀짐
        console.log(`Car - Brand: ${vehicle.brand}, Speed: ${vehicle.speed}`);
    } else {
        // 여기서 vehicle은 Bicycle로 좁혀짐
        console.log(`Bicycle - Brand: ${vehicle.brand}, Type: ${vehicle.type}`);
    }
}

 

위 코드의 isCar 함수는 사용자 정의 타입 가드로, vehicle 이 car 인지 확인하고 해당하는 타입으로 좁혀진다. 

 

4. null & undefined 체크

개발을 하다 보면 어떤 변수나 함수파라미터에 null, undefined가 들어올 경우 어떻게 대처할지 if 문으로 코드를 짜는 경우가 매우 많을 것이다.

if (저 변수가 undefined일 경우) 어쩌구~

 

위와 같은 코드를 작성할 것이다. 왜냐면 저런 상황을 미리 방어하는게 언제나 좋기 때문이다.

 

여기서 && 를 쓰면 위와 같은 if문을 생략할 수 있다.

 

📍 && 연산자란?
원래 &&는 조건식 2개가 참이면 전부 참으로 판정해달라는 논리연산자이다. 하지만 여러개를 사용하면 이상한 현상이 생긴다.

&& 기호로 비교할 때 true와 false를 넣는게 아니라 자료형을 넣으면 && 사이에서 처음 등장하는 falsy 값을 찾아주고 그게 아니면 마지막 값을 남겨준다. falsy 값은 false와 유사한 기능을 하는 null, undefined, NaN 이런 값들을 의미한다.
1 && null && 3   // null이 남음
undefined && '안녕' && 100  // undefined 남음​

 

그래서 && 기호를 이용해서 

if (변수 && typeof strs === "string") {}

 

위 코드와 같이 사용하면 변수가 undefined 면 if 문이 실행되지 않고, (if 문 조건식안에 falsy 값이 남으면 if문 실행되지 않는다.)

변수가 string 타입이면 if 문이 실행된다. 변수가 null, undefined인 경우를 쉽게 거를 수 있는 문법이라고 보면 된다.

 

function printAll(strs: string | undefined) {
  if (strs && typeof strs === "string") {  
    console.log(s);
  } 
}

 

5. in 연산자로 object 자료 narrowing

파라미터로 object가 2개 들어올 수 있다고 타입지정을 해놓았다고 가정해보자.

하나는 { a : 'kim' } 다른 하나는 { b : 'park' } 이렇게 서로 다른 유니크한 속성들을 가지고 있다면 if문을 써도 narrowing이 가능하다.

type Fish = { swim: string };
type Bird = { fly: string };
function 함수(animal: Fish | Bird) {
  if ("swim" in animal) {
    return animal.swim
  }
  return animal.fly
}

 

서로 배타적인 속성을 자겨와야 narrowing이 가능하다.

 

6. literal type narrowing

type Car = {
  wheel : '4개',
  color : string
}
type Bike = {
  wheel : '2개',
  color : string
}

function 함수(x : Car | Bike){
  if (x.wheel === '4개'){
    console.log('이 차는 ' + x.color)
  } else {
    console.log('이 바이크는 ' + x.color)
  }
}

 

위 코드에서 Car, Bike 타입을 각각 만들었다. typeof 연산자를 써도 object라고 나온다. 왜냐하면 typeof 연산자는 string, number, object 이런 것만 구분해주기 때문이다. 하지만 위 코드 처럼 if문으로 조건을 작성하면 narrowing이 충분히 가능하다.

 

🖥️ Type Assertion

타입스크립트에서 Type Assertion은 컴파일러에게 변수의 타읍을 개발자가 미리 지정해주는 것을 의미한다. 개발자가 변수의 타입을 더 확실히 알고 있을 때 사용되며, 특히 다양한 타입 추론 상황에서 명시적으로 타입을 지정하고 싶을 때 유용하다.

 

Type Assertion은 두 가지 형태로 사용된다.

  1. '<타입>' 구문
  2. '값 as 타입' 구문

 

1. '<타입>' 구문

let someValue: any = "hello";
let strLength: number = (<string>someValue).length;

console.log(strLength); // 5

 

2. '값 as 타입' 구문

let someValue: any = "hello";
let strLength: number = (someValue as string).length;

console.log(strLength); // 5

 

이제 몇 가지 실제 예제를 살펴보자.

 

  • 예제 1 : DOM 요소 캐스팅
// HTML 요소 가져오기
let myElement: HTMLElement | null = document.getElementById("myElement");

// Type Assertion을 사용하여 확실히 타입을 지정
if (myElement !== null) {
    (myElement as HTMLInputElement).value = "Hello, TypeScript!";
}
// HTML 요소 가져오기
let myElement: HTMLElement | null = document.getElementById("myElement");

 

여기서 'document.getElementById("myElement")는 HTML 문서에서 'id'가 "myElement"인 요소를 찾아서 변환한다. 그러나 이 요소가 존재하지 않을 수도 있으므로 반환 타입은 'HTMLElement | null' 이다. 따라서 'myElement' 변수는 'HTMLElement' 타입 또는 'null' 일 수 있는 유니온 타입을 가지게 된다.

 

// Type Assertion을 사용하여 확실히 타입을 지정
if (myElement !== null) {
    (myElement as HTMLInputElement).value = "Hello, TypeScript!";
}

 

'myElement'가 'null'이 아닌 경우, 즉 요소가 실제로 존재하는 경우에만 실행되는 조건문이 있다. 이때 'Type Assertion'을 사용하여 'myElement'의 타입을 'HTMLInputElement'으로 지정한다.

 

Type Assertion은 '(myElement as HTMLInputElement)'과 같이 사용되며, 이는 컴파일러에게 "나는 이미 이 변수가 'HTMLInputElement라고 확신한다" 라고 알려주는 역할을 한다. 이후에는 'myElement'를 마치 'HTMLInputElement' 처럼 다를 수 있다.

 

따라서 코드 블록 안에서는 'myElement'가 'HTMLInputElement'로 간주되어, value 속성을 사용하여 해당 요소의 값을 설정할 수 있다.

 

이러한 Type Assertion은 특정 상황에서 컴파일러가 타입을 추론하기 어려운 경우나, 개발자가 이미 타입을 확신할 수 있는 경우에 유용하게 사용된다. 그러나 남용할 경우 코드의 안정성이 저하될 수 있므으로 주의해서 사용해야 한다.'

 

  • 예제 2 : 타입 추론을 해칠 때 Type Assertion 사용
let value: any = "10";
let numberValue: number = value; // Error: Type 'any' is not assignable to type 'number'.

// Type Assertion을 사용하여 명시적으로 타입을 지정
let numberValueAssertion: number = value as number;
console.log(numberValueAssertion); // 10

 

let value: any = "10";

 

여기서 'value' 변수는 'any' 타입으로 선언되어 있다. 'any' 타입은 TypeScript에서 어떤 타입이든 할당할 수 있는 유연한 타입이지만 타입 안정성을 제공하지 않는다. 따라서 'value'에는 문자열, 숫자, 객체 등 어떠한 값이라도 할당할 수 있다.

 

let numberValue: number = value; // Error: Type 'any' is not assignable to type 'number'.

 

하지만 'numberValue' 변수는 명시적으로 'number' 타입으로 선언되어 있으므로, 'value'의 타입이 'number' 가 아니기 때문에 에러가 발생한다. TypeScript는 이러한 명시적인 타입 불일치를 감지하고 에러를 보고한다.

 

// Type Assertion을 사용하여 명시적으로 타입을 지정
let numberValueAssertion: number = value as number;
console.log(numberValueAssertion); // 10

 

여기서 'as'  키워드를 사용한 Type Assertion을 통해 개발자가 'value'가 실제로 'number' 임을 확신하고 있음을 컴파일러에게 알려준다. 따라서 컴파일러는 에러를 발생시키지 않고 코드를 허용한다.

 

그러나 주의해야 할 점은 Type Assertion을 남용하면 타입 안정성이 감소할 수 있기 때문에 가능하면 Type Assertion 없이 타입을 명시적으로 지정하거나 타입을 추론을 활용하는 것이 좋다. 'any' 타입을 최대한 피하고, 타입 안정성을 유지하는 것이 좋다.

 

728x90