타입스크립트 타입추론, 타입단언, 타입가드, 타입호환 개념 정리

타입추론, 타입단언, 타입가드, 타입호환

🔵 타입 추론

- 타입을 따로 지정하지 않아도 알아서 추론하는 것

 

기본적인 타입추론

// 기본적인 타입추론
var a; // 이미 any로 추론함
var b = 10; // number로 추론
var c = 'abc' // string으로 추론

 

 

함수에 타입추론

// 함수에 타입추론
function getA(a) {
    return a;  
}
// ==> any를 받고 any를 리턴하도록 추론

function getB(b = 10) {
    var c = 'abc';
    return b + c;
}
// ==> number를 받고 string를 리턴하도록 추론

 

제네릭으로 정의한 인터페이스에 타입추론

// 제네릭으로 정의한 인터페이스에 타입추론
interface Dropdown<T> {
    value: T;
    title: string;
}

var shoppingItem: Dropdown<string> = {
    // string을 넣었으니 이제 value는 string으로 타입추론함
    value: 'abc',
    title: 'abc'
}

 

가장 적절한 타입(Best Common Type)

let x = 3;
let arr = [0, 1, null];

- 해당 코드가 어떤 타입인지 타입스크립트 가 알아서 골라준다.

 

여기서 x는 number로 추론

arr은 number와 null이 있기 때문에  number | null로 유니온타입으로 추론

 

* typescript Language Server : VSCode의 타입스크립트 언어 서버에서 코드를 분석하고 타입 체크를 수행하기 때문에 타입스크립트 파일에서 변수를 선언하면 VSCode 내부에서 변수의 타입 추론이 가능한 것!


🔵 타입 단언

- as 타입

- 타입스크립트보다 개발자가 타입을 더 잘 아는 경우 사용

- 주로 DOM API 조작할 때 사용된다.

- 이러한 타입 단언을 조건문에 사용하면 타입 단언이 반복되어 가독성을 망칠 수 있음 (이럴때 타입가드를 활용)

// 타입단언
var a; // 추론으로 인해 any 타입
a = 'hi';

// var b = a; // ==> 문자열임을 알지만 b도 any로 추론한다.

// 타입스크립트는 모르지만 우리는 string인 것을 알기때문에 타입단언
var b = a as string;

 

 타입단언으로 DOM API 조작 예시

// <div id='app' > hi < /div> 이렇게 DOM이 구성되어있을 경우
var div = document.querySelector('div'); // HTMLDivElement | null 타입
div.innerText ==> 에러! null 일수도 있어서!

DOM 요소를 가져왔을 경우 알아서 HTMLDiveElement | null 타입으로 추론하게된다.

즉, null일수도 있기 때문에 속성 내부로 접근하게 될 경우 에러가 발생한다.

 

해결방법1. 그냥 조건문이나 옵셔널 체이닝을 활용하여 요소를 확인한다.

// div 확인1
if (div) {
    div.innerText
}

// div 확인2
div?.innerText;

 

해결방법2. 타입단언

var div = document.querySelector('.div') as HTMLDivElement;
div.innerHTML

🔵 타입 가드

- 유니온 타입일 경우 각 타입을 확인해야하는 과정을 거쳐야함

- 타입가드 함수를 활용해서 코드 가독성 살리기

 

interface Dev {
  name: string;
  skill: string;
}

interface Per {
  name: string;
  age: number;
}

function introduce(): Dev | Per {
  return { name: 'Tay', age: 30, skill: 'TS' }
}

var tay = introduce();

console.log(tay.name); // ==> OK!
console.log(tay.skill); // ==> 에러! 공통 속성이 아니라서 에러

유니온타입으로 인해 Dev와 Per의 공통적인 속성에만 접근이 가능하다.

즉, 다른 타입에 대한 '보장' 이 필요하다.

 

타입 단언으로 타입에 대한 보장을 해주자

if ((tay as Dev).skill) {
  console.log((tay as Dev).skill);
} else if ((tay as Per).age) {
  console.log((tay as Per).age);
}

타입 단언의 늪;;; 반복적인 타입 단언으로 코드 가독성을 망친다.

이럴때 타입가드가 필요!

 

타입 가드의 여러 방법

1. 사용자 정의 타입 가드

// 타입을 판단하는 함수
function isDev(target: Dev | Per): target is Dev {
    return (target as Dev).skill !== undefined;
}

- 해당 속성의 유무에 따라 true false 값이 반환된다.

- 인터페이스 등 복잡한 타입에 활용.

 

해당 타입 가드를 적용하였을 경우

if (isDev(tay)) {
    console.log(tay.skill);
} else {
    console.log(tay.age);
}

- 타입 판단 로직을 재사용하여 가독성을 높인다.

 

2. typeof 활용

const isNumber = (target: number | string) => {
    if (typeof target === 'number') {
    	// toString은 number 메서드
    	console.log(target.toString);
    }
};

- 간단한 타입에 적용하기 좋음

 

3. instanceof 활용

class Dev {
    common = '123';
    dev = 123;
}

class Per {
    common = '123';
    per = 123;
}

const devorper = (target: Dev | Per) => {
	// Dev 클래스의 속성일 경우
    if (target instanceof Dev) {
        console.log(target.dev);
    }
    // Per 클래스의 속성일 경우
    if (target instanceof Per) {
        console.log(target.per)
    }
    // 공통 속성일 경우
    console.log(target.common);
};

devorper(new Dev());
devorper(new Per());

- 객체가 어디 클래스에 속하는지 확인

* 한 파일 당 한 클래스만 사용 권장.. (잘못된 구조 가능성)

 

4. in 활용

interface Human {
    talk: () => void;
}

interface Dog {
    tail: string;
    bark: () => void;
}

const comunicate = (target: Human | Dog) => {
    if ('tail' in target) {
        // tail이 있으면 Dog니까
        target.bark();
    } else {
        target.talk();
    }
};

- 객체 내부에 특정 프로퍼티가 존재하는지 확인

 

5. 리터럴 타입

// 동등, 일치 연산자
type States = 'a' | 'b' | 'c';
const whattype = (state: States) => {
  if (state === 'a') {
    console.log('a타입이에요')
  } else if (state === 'b') {
    console.log('b타입이에요')
  } else {
    console.log('c타입이에요')
  }
};
// switch
const whattype2 = (state: States) => {
  switch (state) {
    case 'a':
      console.log('a타입이에요')
      break;
    case 'b':
      console.log('b타입이에요')
      break;
    case 'c':
      console.log('c타입이에요')
      break;
  }
};

- 동등,일치 연산자(===,==,!==,!= ), switch 활용


🔵 타입 호환

- 타입스크립트가 코드를 해석하는 과정에서 특정 타입이 다른 타입과 잘 맞는지 확인하는 것

- 인터페이스간 타입을 확인하는 것이 아닌 타입에 정의되어 있는 '속성'을 활용함

 

interface Person {
    name: string;
}

class Dev {
    name: string;
}

let i: Person; // 타입은 Person으로 정의
i = new Dev(); // Dev로 생성
// 내부 구조가 똑같아서 OK

- 항상 타입이 같지 않아도 된다. (interface, class)

- 내부적 구조로만 판단한다는 개념이 중요

제네릭과 인터페이스, 클래스 등 내부적 구조가 *같아야만 호환이 가능

- 함수같은 경우는 정의된 속성이 많은 쪽이 호환이 유연하다.

 


 

참고 강의 : https://taylog.tistory.com/217

 

[강의리뷰] 실전 프로젝트로 배우는 타입스크립트 시리즈 강의 후기 (캡틴판교) 😱 타입스크립

캡틴판교 타입스크립트 시리즈 강의 후기 이번엔 타입스크립트 강의 후기입니다! 회사에서 타입스크립트를 사용할 일이 생겨서 이 전에 사둔 타입스크립트 강의를 드디어 수강완료했습니다!

taylog.tistory.com