*이 게시물은 이정환 강사님의 '한 입 크기로 잘라먹는 타입스크립트' 강의를 수강한 뒤 정리한 글입니다.
타입 호환성
- 서로 다른 타입이 있을 때, 각자를 해당 타입으로 취급해도 되는지 판단하는 것.
- 타입간의 집합 관계도를 생각하면 이해하기 쉬움.
- TS는 슈퍼타입의 값을 서브타입의 값으로 취급하는 걸 허용하지 않는다! (반대는 성립 가능)
- ex) a: Number, b:Number Literal 일 때, b는 number로 취급할 수 있지만 a는 넘버리터럴 타입으로 취급할 수 없다!
- 대부분의 경우엔 다운캐스팅을 허용하지 않는다.
let num1 : number = 10;
let num2 : 10 = 10;
num1 = num2 // 넘버리터럴 값을 넘버에 할당하겠다(업캐스팅) -> 가능
num2 = num1 // 넘버값을 넘버 리터럴에 할당하겠다(다운캐스팅) -> 호환 x, 오류!

※ 업캐스팅/다운캐스팅 여부가 헷갈릴 때는 이 타입계층도를 떠올리면 쉽다!
- 모든 타입은 unknown 타입으로 업캐스팅 가능하다(=unknown 타입엔 어떤 값이든 담을 수 있다)
- 모든 타입은 never 타입으로 다운캐스팅 할 수 없다. (= never에 값을 할당할 수 없다)
- void는 undefined 값을 반환해도 괜찮지만, undefined 타입은 void 값을 반환할 수 없다
- any는 계층구조도를 모두 무시한다! 그렇기에 최대한 쓰지 않는게 좋다
객체 타입의 호환성
- 기본타입과 동일하게 객체타입의 호환성도 슈퍼-서브 간의 관계로 판단함.
- 더 많은 프로퍼티를 가지고 있는 쪽이 서브! (부분 집합)
// 객체 타입간 호환성 예시
type Animal = {
name: string;
color: string;
}
type Dog = {
name: string;
color: string;
breed: string;
}
let animal: Animal = {
name: "기린",
color: "yellow"
}
let dog: Dog = {
name: "돌돌이",
color: "brown",
breed: "월월"
}
animal = dog // 정상
dog = animal // 오류!
※ 업캐스팅이라도 초과 프로퍼티 검사가 발동하면 에러가 날 수 있다!
- 초과 프로퍼티 검사란? : 객체 리터럴로 초기화 할 때 발동하는 TS 기능. 타입에 정의된 프로터피 외의 다른 초과 프로퍼티를 갖는 객체를 변수에 할당할 수 없게 막는 역할.
// 초과프로퍼티 예시
type Book = {
name: string;
price: number;
}
type ProgrammingBook = {
name: string;
price: number;
skill: string
}
let book: Book = {
name: "한 입 크기로 잘라먹는 타입스크립트",
price: 33000,
skill: "typescript" // 에러!
}
- 해결 방법 : 객체 리터럴을 사용하지 않으면 됨 => 값을 별도의 변수에 보관한 다음, 그 변수값을 초기화 값으로 사용하면 됨.
// 초과프로퍼티 예시 - Ver. 에러 수정
type Book = {
name: string;
price: number;
}
type ProgrammingBook = {
name: string;
price: number;
skill: string
}
// 1. 변수값 보관
let programmingBook: ProgrammingBook = {
name: "한 입 크기로 잘라먹는 타입스크립트",
price: 33000,
skill: "타입스크립트"
}
// 2. 보관한 값으로 초기화
let book: Book = programmingBook;
// 함수의 매개변수에도 초과 프로퍼티 검사는 동일하게 발생
// 같은 방식으로 문제 해결
(...)
function func(book: Book){}
func({
name: "한 입 크기로 잘라먹는 타입스크립트",
price: 33000,
skill: "타입스크립트" // 에러!
})
// 해결책
func(programmingBook);
대수타입
여러 타입을 합성하여 만든 타입.
1. 합집합(Union) 타입
- '|' 를 사용하여 여러개의 타입을 정의함. (ex) let a = string | number
- 합칠 수 있는 타입 갯수 제한 X
- 배열, 객체 등 다방면으로 사용 가능함
// 유니온 배열 타입
// 배열에서 유니온 사용할 땐 타입정의 부분을 () 로 묶는다!
let arr: (number | string | boolean)[] = [1, "hello", false]
// 유니온 객체 타입
type Dog = {
name : string;
color: string;
}
type Person = {
name : string;
language : string;
}
type UnionEx: Dog | Person; // Dog 타입과 Person 타입의 합집합-> 모든 원소 사용 가능
let union1: Union1 = { // 정상
name: "",
color: "",
};
let union2: Union1 = { // 정상
name: "",
language: "",
};
let union3: Union1 = { // 정상
name: "",
color: "",
language: "",
};
let union4: Union1 = { // 에러!
name: "",
};
2. 교집합(Intersection) 타입
- '&' 를 사용하여 교집합 정의. (ex) let b = number & string
- 기본 타입들은 보통 서로소 관계이기 때문에 교집합이 존재하지 않음 => never 타입으로 추론 됨
- 위의 이유로 주로 객체 타입에서 사용되는 편
type Dog = {
name: string;
color: string;
}
type Person = {
name: string;
language: string;
}
type Intersection: Dog & Person;
// 질문
let intersection: Intersection = {
name: "",
color: "",
language: ""
}
타입 추론
- 타입이 정의되어 있지 않은 변수의 타입을 자동으로 추론하는 것.
- 일반적으론 초기값을 기준으로 판단. 함수의 경우엔 return 값을 기준으로 판단.
- '함수의 매개변수' 같이 일부 상황에서는 추론하지 못함
-> any 타입으로 추론됨. 이 경우엔 strict 옵션에서 오류로 판단됨.
-> 일반 변수의 타입이 any 로 판단될 경우에는 strict 옵션에서 걸리지 않음. - let 과 const에 따라 추론되는 타입이 다름
- let num1 = 10 -> number 타입으로 추론
- const num2 = 10 -> number literal 타입으로 추론
- 배열의 경우, 다양한 타입값을 담을 시 최적의 공통 타입(Best Common Type) 으로 추론됨
- let arr = [1, 'string'] -> (string | number)[] 값으로 추론
타입 단언
- 특정 값을 원하는 타입으로 단언하는 기능. '값 as 타입' 으로 정의
- 사용 조건: A as B의 형태일 때, A와 B는 호환되어야 한다. (number as string -> 에러!)
- 어떨 때 사용할까?
- 타입 정의한 객체의 초기값을 빈 값으로 두고 싶을 때
- 초과 프로퍼티 검사를 피할 때
// 1. 타입 정의한 객체의 초기값을 빈 값으로 두고 싶을 때
type Person = {
name: string;
age: number;
}
// Person 타입은 빈 객체가 아니므로 오류가 발생한다
let person: Person = {}; //에러
person.name = "";
person.age = 23;
// 타입 단언 사용
let person {} as Person; // 정상
person.name = "";
person.age = 23
// 2. 초과 프로퍼티 검사를 피할 때
type Dog = {
name: string;
color: string;
}
let dog: Dog = {
name: "돌돌이",
color: "brwon",
breed: "진도"
// Dog 타입에는 없는 프로퍼티. 하지만 타입 단언을 사용했기 때문에 초과 프로퍼티 검사에 걸리지 않는다.
} as Dog
다중 단언 (사용 비추천)
let num = 10 as unknown as string
- 단언 순서: 왼쪽에서 오른쪽.
- 실제로 값을 해당 타입으로 변환시키는 게 아닌 단순 눈속임으로, 슈퍼-서브 관계가 아닌 타입 단언 시 오류가 발생할 수 있다!
const 단언
- 타입 단언때만 사용할 수 있는 형식.
- 특정 값을 const 타입으로 단언하면 변수를 const로 선언한 것 처럼 타입이 변경됨.
let num = 10 as const;
// num 의 타입은 10 Number Literal 으로 단언됨.
let cat = {
name : "야옹이",
color: "yellow"
} as const
// 모든 프로퍼티가 readonly 를 갖도록 단언됨
Non Null 단언
- 값 as 타입 의 형태를 따르지 않는 단언.
- 값 뒤에 !를 붙이면 해당 값을 undefined나 null이 아닐 것으로 단언할 수 있음
type Post = {
title: string;
author?. string;
}
let post: Post = {
title: "게시글1"
}
const len: number = post.author!.length
// post.author 값은 null이나 undefined가 아니다!
타입 좁히기
- 유니온 타입으로 선언했을 경우, 각 타입에 맞는 메소드를 오류 없이 사용할 수 있도록 타입을 좁혀주는 것.
- 조건문과 typeof 를 사용. 이런 형태를 '타입 가드'라고 부름.
function func(value: number | string) {
if(typeof value === "number") {
console.log(value.toFixed());
}
if(typeof value === "string"){
console.log(value.toUpperCase());
}
}
1. instanceof 타입 가드
- 내장 클래스 타입을 보장할 수 있는 타입가드 만드는 형식
- 내장 클래스 또는 직접 만든 클래스에만 사용 가능. 직접 만든 타입에는 사용 X
function func(value: number | string | Date | null) {
if (typeof value === "number") {
console.log(value.toFixed());
} else if (typeof value === "string") {
console.log(value.toUpperCase());
} else if (value instanceof Date) {
console.log(value.getTime());
}
}
2. in 타입 가드
- 직접 만든 타입에 사용하고 싶을 때 사용하는 연산
// 직접 만든 타입
type Person = {
name: string;
age: number;
};
function func(value: number | string | Date | null | Person) {
if (typeof value === "number") {
console.log(value.toFixed());
} else if (typeof value === "string") {
console.log(value.toUpperCase());
} else if (value instanceof Date) {
console.log(value.getTime());
} else if (value && "age" in value) {
console.log(`${value.name}은 ${value.age}살 입니다`)
}
}
서로소 유니온 타입
- 서로소 관계(교집합 x)인 타입들을 모아 만든 유니온 타입
type Admin= {
name: string;
kickCount: number;
tag: "ADMIN";
}
type Member = {
name: string;
point: number;
tag: "MEMBER";
}
type Guest = {
name: string;
visitCount: number;
tag: "GUEST";
}
type User = Admin | Member | Guset; // User는 '서로소 유니온 타입'
// 활용법
// 타입 좁히기의 일환으로 tag라는 리터럴 프로퍼티를 만들었음
function login(user:User){
swith(user.tag){
case: "ADMIN": {
console.log(`${user.name}님, 현재까지 ${kickCount}명 퇴장시켰습니다.`);
}
case: "MEMBER"{
console.log(`${user.name}님, 현재까지 모은 포인트는 ${point}점 입니다.`)
}
case : "GUEST" {
console.log(`${user.name}님, 현재까지 ${name.visitCount}번 방문하셨습니다.`)
}
}
}'개발공부' 카테고리의 다른 글
| #6. 클래스 (0) | 2025.02.27 |
|---|---|
| #5. 인터페이스 (0) | 2025.02.26 |
| #4. 함수와 타입 (0) | 2025.02.25 |
| #2. 타입스크립트 기본 (0) | 2025.02.18 |
| #1. 타입스크립트 개론 (0) | 2025.02.17 |