타입추론, 타입단언, 타입가드, 타입호환
🔵 타입 추론
- 타입을 따로 지정하지 않아도 알아서 추론하는 것
기본적인 타입추론
// 기본적인 타입추론
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
'🧠 저장 > Typescript' 카테고리의 다른 글
타입스크립트 유틸리티 타입 (파셜, 픽, 오밋) 정리 (0) | 2024.03.21 |
---|---|
타입스크립트 제네릭 선언, 타입제한 간단 정리 (0) | 2024.03.15 |
타입스크립트 연산자, 이넘, 클래스 활용방법 간단 정리 (0) | 2024.03.12 |
인덱싱 패턴과 딕셔너리 패턴 (0) | 2024.03.09 |
타입스크립트 인터페이스와 타입별칭 특징과 차이점 (0) | 2024.03.06 |