이끌든지 따르든지 비키든지

Software Development/TypeScript

[TypeScript] 타입 추론 & 타입 단언 & 타입 가드

SeongHo5 2023. 10. 4. 21:48

타입 추론(Type Inference)

타입스크립트는 코드를 작성할 때 변수, 함수, 매개변수 및 반환값 등에 명시적으로 타입을 선언할 수 있다.

그러나 타입을 명시적으로 선언하지 않아도  컴파일러가 코드를 분석하고 타입을 추론할 수 있는데, 이를 타입 추론이라고 한다.

 

function add(a: number, b: number) {
    return a + b;
}

const result = add(10, 20);  // result가 number 타입일 것으로 추론한다.

 

예를 들어, 위 코드는 매개변수 a와 b가 number 타입임을 명시했으므로, 컴파일러는 이를 바탕으로 result가  number 타입일 것으로 추론한다.

 

const user = {
  name: 'John Doe',
  age: 30,
};

const { name, age } = user;  // name은 string, age는 number로 추론함

 

위 코드처럼, 구조 분해 코드에서도 각각의 속성을 추론할 수 있다.

 


 

근데, 타입스크립트는 엄격한 타입 검사로 오류를 줄이자는 취지로 만든 언어 아니었나요?   

라고 물어볼 수 있다.

 

타입스크립트의 타입 추론은 엄격한 타입 검사에 약간의 유연성을 더한 것이라 볼 수 있다. 아래 코드 예시를 보자.

 

// 변수 초기화
const numbers: number[] = [1, 2, 3, 4, 5];

const numbers = [1, 2, 3, 4, 5];

 

이 코드에서 numbers 변수는 굳이 타입 명시를 하지 않아도 number[] 타입임을 추론할 수 있다.

 

// 타입 추론 사용
const greet = (name: string) => {
    return `Hello, ${name}!`;
};

const greeting = greet('Alice');

// 타입 추론 사용 x
const greet: (name: string) => string = (name) => {
    return `Hello, ${name}!`;
};

const greeting: string = greet('Alice');

 

화살표 함수로 작성된 이 코드에서도, 모든 객체에 대한 타입 명시가 없어도 매개변수의 타입을 통해 greeting 객체가 string 타입임을 추론할 수 있다.

 

이처럼, 타입 추론 적절히 사용하면 안정성과 생산성을 적절히 유지하면서도, 개발자가 모든 객체에 직접 타입을 명시해야 하는 부담을 줄여 유연성을 추구할 수 있다.

 

 


 

타입 단언(Type Assertion)

 

컴파일러가 코드 맥락으로 타입을 추론하는 걸 타입 추론이라 한다면, 반대로 "이 변수의 타입은 무조건 이거다 반박 시 내 말이 맞음"을 알리는 방법은 없을까?

 

"이 변수는 내가 명시한 타입이 무조건 맞다"고 컴파일러에게 알려주는 것을 타입 단언 또는 타입 표명이라 하는데, 주로 컴파일러가 실제 런타임에 존재하는 변수 타입과 다르게 추론하거나, 너무 보수적으로 추론할 때 타입을 명시하기 위해 사용한다.

 

타입 단언을 선언하는 방법은 2가지가 있는데,

 

  1. as 구문을 사용하거나,
  2. < > (앵글 브래킷 기호)를 사용하는 것

이다.

 

 

as 구문을 사용할 경우

const value: any = 'hello';
const length: number = (value as string).length; // value를 string으로 타입 단언

 

 

<> 기호를 사용할 경우

<>를 사용한 타입 단언은 제네릭(Generic)이나, 리액트 Tag와 구분이 어려울 수도 있으니 웬만하면 as를 사용하자

const value: any = 'hello';
const length: number = (<string>value).length;

 

위 코드에서 value 객체는 any 타입으로 선언되어 컴파일러가 타입을 추론하지 않는다.

다음 줄에서 length를 계산할 때, string 타입으로 타입 단언된 value의 length를 계산해서 초기화한다.

 

타입 단언은 C나 JAVA에서의 강제 형변환과는 다른 개념이다.
강제 형변환은 값을 실제로 변환하지만, 타입 단언은 그냥 어떤 타입인지를 컴파일러에게 알려줄 뿐이다.
편해 보이지만 타입 단언을 남발하면 지옥이 기다린다고 하니 쓰더라도 적당히 쓰자.

 


 

타입 가드(Type Guard)

 

타입 가드는 조건문이나 키워드를 사용해서 변수의 타입을 좁혀줘 컴파일러가 좀 더 정확히 타입을 추론하고, 오류를 줄일 수 있도록 하는 메커니즘, 코드 작성 기법을 말한다.

(새로운 키워드나, 문법을 사용하는게 아니고 원래 있던 걸 활용해서 오류를 줄이는 방법임!)

 

타입 가드를 위해 주로 아래 키워드들과 조건문(if문)을 조합해 코드를 작성한다.

 

  • typeof
function printValue(value: string | number) {
    if (typeof value === 'string') {
        console.log(value.toUpperCase());
    } else {
        console.log(value.toFixed(2));
    }
}

printValue('hello');  // 출력: HELLO
printValue(42);       // 출력: 42.00

 

typeof 연산자로 value의 타입을 검사해 문자열인지, 숫자인지에 따라 toUpperCase(), toFixed()를 사용할 수 있다.

 

타입 가드로 오류도 줄이고, 한 함수 안에서 타입에 따라 다른 동작을 실행할 수도 있는 것!

 

  •  Array.isArray()

배열인지 아닌지 검사할 때 사용할  수 있다.

 

  • .type / In

객체의 속성 값에 대한 타입을 검사할 때 사용할 수 있다.

 

  • instanceof
class Animal {
    sound() {
        console.log('Making a sound');
    }
}

class Dog extends Animal {
    bark() {
        console.log('Woof!');
    }
}

class Cat extends Animal {
    meow() {
        console.log('Meow!');
    }
}

function makeSound(animal: Animal) {
    if (animal instanceof Dog) {
        animal.bark();
    } else if (animal instanceof Cat) {
        animal.meow();
    } else {
        animal.sound();
    }
}

const dog = new Dog();
const cat = new Cat();

makeSound(dog);  // 출력: Woof!
makeSound(cat);  // 출력: Meow!

 

instanceof 연산자를 통해 어떤 클래스의 인스턴스인지 검사하고, 해당하는 클래스의 메서드를 호출해 실행할 수 있다.


 

사용자 정의 타입 가드

 

위에서 본 타입 검사 연산자보다 조금 더 자세한 타입 검가 필요하거나, 로직이 복잡한 경우 is [Type]을 리턴하도록 사용자 정의 타입 가드를 만들 수 있다.

 

interface Car {
    brand: string;
}

interface Bike {
    brand: string;
    gears: number;
}

// 사용자 정의 타입 가드
function isCar(vehicle: Car | Bike): vehicle is Car {
    return (vehicle as Car).brand !== undefined;
}

function printVehicleInfo(vehicle: Car | Bike) {
    if (isCar(vehicle)) {
        console.log('브랜드명 :', vehicle.brand);
    } else {
        console.log('브랜드명 :', vehicle.brand, '기어수:', vehicle.gears);
    }
}

const car: Car = { brand: 'Toyota' };
const bike: Bike = { brand: 'Honda', gears: 6 };

printVehicleInfo(car);  // 출력: 브랜드명 : Toyota
printVehicleInfo(bike); // 출력: 브랜드명: 기어수: 6

 

isCar라는 사용자 정의 타입 가드로  vehicle이 Car 타입인지 확인하고, 그 결과에 따라 boolean 값을 반환한다.

이 리턴값으로 vehicle이 Car인지 Bike인지 타입을 확인하고, 다른 동작을 수행할 수 있다.

'Software Development > TypeScript' 카테고리의 다른 글

[TypeScript] TypeScript란?  (0) 2023.09.30