타입스크립트 이해하기
타인은 집합이다
슈퍼타입(부모타입) > 서브타입(자식타입)
타입 계층도와 함께 기본타입 살펴보기
/**
* unknown 타입
*/
function unknownExam() {
// up cast
let a: unknown = 1;
let b: unknown = "hello";
let c: unknown = true;
let d: unknown = null;
let e: unknown = undefined;
let unknownVar: unknown;
// down cast X
// let num: number = unknownVar;
// let str: string = unknownVar;
// let bool: boolean = unknownVar;
}
/**
* never 타입
* 공집합
*/
function neverExam() {
function neverFunc(): never {
while (true) {}
}
let num: number = neverFunc();
let str: string = neverFunc();
let bool: boolean = neverFunc();
// let never1: never = 10;
// let never2: never = "string";
// let never3: never = true;
}
/**
* void 타입
* undefined의 슈퍼타입
*/
function voidExam() {
function voidFunc(): void {
console.log("hi");
return undefined;
}
let voidVar: void = undefined;
}
/*
* any 타입
*/
function anyExam() {
let unknownVar: unknown;
let anyVar: any;
let undefinedVar: undefined;
let neverVar: never;
anyVar = unknownVar;
// any - down cast 가능 => 타입 계층도 다 무시하는 타입
undefinedVar = anyVar;
anyVar = unknownVar;
// 예외) never타입은 공집합이므로 어떤 타입도 down cast 할 수 없음
// neverVar = anyVar;
}
객체 타입의 호환성
/*
* 객체 타입간의 호환성
* => 어떤 객체타입을 다른 객체 타입으로 취급해도 괜찮은가?
*/
// 추가 property가 없는 type이 super type
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: "진도",
};
// down cast
animal = dog;
// up cast
// dog = animal;
type Book = {
name: string;
price: number;
};
type ProgrammingBook = {
name: string;
price: number;
skill: string;
};
let book: Book;
let programmingBook: ProgrammingBook = {
name: "타입스크립트",
price: 10000,
skill: "typescript",
};
/*
* 초과 프로퍼티 검사
*/
book = programmingBook;
// programmingBook = book;
let book2: Book = {
name: "타입스크립트",
price: 10000,
// skill: "typescript",
};
let book3: Book = programmingBook;
function func(book: Book) {}
func({
name: "타입스크립트",
price: 10000,
// skill: "typescript",
});
// 인수로 변수 전달
func(programmingBook);
대수 타입
/*
* 대수 타입
=> 여러 개의 타입을 합성해서 새롭게 만들어낸 타입
=> 합집합 타입과 교집합 타입이 존재
*/
/*
* 1. 합집합 - Union 타입
*/
let a: string | number | boolean;
a = 1;
a = "hello";
a = true;
let arr: (number | string | boolean)[] = [1, "hello", true];
type Dog = {
name: string;
color: string;
};
type Person = {
name: string;
language: string;
};
type Union1 = Dog | Person;
let union1: Union1 = {
name: "",
color: "",
};
let union2: Union1 = {
name: "",
language: "",
};
let union3: Union1 = {
name: "",
color: "",
language: "",
};
// let union4: Union1 = {
// name: "",
// }
/*
* 2. 교집합 타입 - Intersection 타입
*/
// never 타입 => 공집합
let variable: number & string;
type Intersection = Dog & Person;
// Dog와 Person의 모든 property를 포함해야 교집합(&) 타입으로 포함될 수 있다
let intersection1: Intersection = {
name: "",
color: "",
language: "",
};
타입 추론
/*
* 타입 추론
*/
// let a: number = 10;
// 자동으로 변수 타입 추론
let a = 10;
let b = "hello";
let c = {
id: 1,
name: "rachel",
profile: {
nickname: "rachel_pink",
},
urls: [""],
};
let { id, name, profile } = c;
let [one, two, three] = [1, "hello", true];
// 매개 변수는 자동으로 타입 추론 x => 타입 명시해주지 않으면 오류 발생
// function func(param){
// }
// 함수의 반환값 추론 o
// => 추론할 정보가 있으면
function func1() {
return "hello";
}
function func2(message = "hello") {
return "hello";
}
// any 타입의 진화
let d;
d = 10;
d.toFixed();
d = "hello";
d.toUpperCase();
// const로 선언한 변수는 상수이기 때문에 number 리터럴, string 리터럴로 추론
const num = 10;
const str = "hello";
let arr = [1, "string"];
타입 단언
/**
* 타입 단언(assertion)
*/
type Person = {
name: string;
age: number;
};
let person = {} as Person;
person.name = "";
person.age = 25;
type Dog = {
name: string;
color: string;
};
// 초과 프로퍼티 문제 해결됨
let dog = {
name: "돌돌이",
color: "brown",
breed: "진도",
} as Dog;
/**
* 타입 단언의 규칙
* 값 as 단언 <- 단언식
* A as B
* A가 B의 슈퍼타입이거나
* A가 B의 서브타입이어야 함
*/
let num1 = 10 as never;
let num2 = 10 as unknown;
// 서로 겹치는 부분이 조금이라도 있어야 타입 단언 가능!
// let num3 = 10 as string;
// unknown으로 다중 단언을 하면 가능하지만 되도록 지양
let num3 = 10 as unknown as string;
/**
* const 단언
*/
let num4 = 10 as const;
// 객체를 as const로 단언하면 모든 property를 readonly로 만듬
let cat = {
name: "야옹이",
color: "yellow",
} as const;
/**
* Non Null 단언
*/
type Post = {
title: string;
// 옵셔녈 체이닝
author?: string;
};
let post: Post = {
title: "게시글1",
author: "rachel",
};
// const len: number = post.author?.length;
const len: number = post.author!.length;
업 캐스팅 - 모든 상황에 가능
다운 캐스팅 - 대부분의 상황에 x
타입 좁히기
/**
* 타입 좁히기
* 조건문 등을 이용해 넓은 타입에서 좁은 타입으로
* 타입을 상황에 따라 좁히는 방법을 이야기
*/
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}살 입니다`);
}
}
서로소 유니온 타입
/**
* 서로소 유니온 타입
* 교집합이 없는 타입들로만 만든(서로소) 유니온 타입을 말함
*/
type Admin = {
tag: "ADMIN";
name: string;
kickCount: number;
};
type Member = {
tag: "MEMBER";
name: string;
point: number;
};
type Guest = {
tag: "GUEST";
name: string;
visitCount: number;
};
type User = Admin | Member | Guest;
// function login(user: User) {
// if ("kickCount" in user) {
// // admin 타입
// console.log(`${user.name}님 현재까지 ${user.kickCount}명 강퇴헸습니다.`);
// } else if ("point" in user) {
// // member 타입
// console.log(`${user.name}님 현재까지 ${user.point} 모았습니다.`);
// } else {
// // guest 타입
// console.log(`${user.name}님 현재까지 ${user.visitCount}번 방문하셨습니다.`);
// }
// }
// 주석이 없으면 직관적으로 알 수 없는 위 코드를 tag property를 추가해 변경
// tag를 추가하면 string 리터럴 타입이 추가되어 서로소 유니온 타입이 됨
function login(user: User) {
if (user.tag == "ADMIN") {
// admin 타입
console.log(`${user.name}님 현재까지 ${user.kickCount}명 강퇴헸습니다.`);
} else if (user.tag == "MEMBER") {
// member 타입
console.log(`${user.name}님 현재까지 ${user.point} 모았습니다.`);
} else {
// guest 타입
console.log(`${user.name}님 현재까지 ${user.visitCount}번 방문하셨습니다.`);
}
}
// 혹은 switch문 이용
function login2(user: User) {
switch (user.tag) {
case "ADMIN": {
console.log(`${user.name}님 현재까지 ${user.kickCount}명 강퇴헸습니다.`);
break;
}
case "MEMBER": {
console.log(`${user.name}님 현재까지 ${user.point} 모았습니다.`);
break;
}
case "GUEST": {
console.log(
`${user.name}님 현재까지 ${user.visitCount}번 방문하셨습니다.`
);
break;
}
}
}
/**
* 복습겸 한가지 더 사례
*/
// 비동기 작업의 결과를 처리하는 객체
type LoadingTask = {
state: "LOADING";
};
type FailedTask = {
state: "FAILED";
error: {
message: string;
};
};
type SuccessTask = {
state: "SUCCESS";
response: {
data: string;
};
};
type AsyncTask = LoadingTask | FailedTask | SuccessTask;
function processResult(task: AsyncTask) {
switch (task.state) {
case "LOADING": {
console.log("로딩 중");
break;
}
case "FAILED": {
console.log(`에러 발생 : ${task.error.message}`);
break;
}
case "SUCCESS": {
console.log(`성공 : ${task.response.data}`);
break;
}
}
}
const loading: AsyncTask = {
state: "LOADING",
};
const failed: AsyncTask = {
state: "FAILED",
error: {
message: "오류 발생 원인은 ~~",
},
};
const success: AsyncTask = {
state: "SUCCESS",
response: {
data: "데이터 ~~",
},
};
[참고]
- Section 3. 타입스크립트 이해하기 강의를 듣고 정리한 내용입니다.
'프로그래밍 > TypeScript' 카테고리의 다른 글
타입스크립트 클래스 (1) | 2023.10.23 |
---|---|
타입스크립트 인터페이스 (0) | 2023.10.23 |
타입스크립트 함수와 타입 (0) | 2023.10.18 |
타입스크립트 기본 (1) | 2023.10.07 |
TypeError [ERR_UNKNOWN_FILE_EXTENSION] Unknown file extension ".ts” 해결 (0) | 2023.10.04 |