let num1 : number = 10;
let num2 : 10 = 10;
num1 = num2; //업캐스팅
num2 = num1; //에러
2. 타입 계층도
/**
* Unknown 타입 : 전체 집합 (모든 타입들을 포함)
*/
function unknownExam() {
//업캐스팅 (가능)
let a: unknown = 1;
let b: unknown = "hello";
let c: unknown = true;
let d: unknown = null;
let e: unknown = undefined;
let unknownVar: unknown;
//다운캐스팅 (에러)
// 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 타입
*/
function voidExam() {
function voidFunc(): void {
console.log("hi");
return undefined;
}
let voidVar: void = undefined;
}
/**
* any 타입 : never 제외 다운캐스팅 모두 가능
*/
function anyExam() {
let unknownVar: unknown;
let anyVar: any;
let undefinedVar: undefined;
let neverVar: never;
//다운캐스팅 가능
anyVar = unknownVar;
undefinedVar = anyVar;
//never은 어느타입도 다운캐스팅 불가 (any도 불가)
//neverVar = anyVar;
}
3. 객체 타입의 호환성
/**
* 기본 타입간의 호환성
*/
let num1: number = 10;
let num2: 10 = 10;
num1 = num2;
/**
* 객체 타입간의 호환성
* -> 어떤 객체 타입을 다른 객체 타입으로 취급해도 괜찮은가?
*/
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;
type Book = {
name: string;
price: number;
};
type ProgrammingBook = {
name: string;
price: number;
skill: string;
};
let book: Book;
let programmingBook: ProgrammingBook = {
name: "한 입 크기고 잘라먹는 리액트",
price: 33000,
skill: "reactjs",
};
book = programmingBook;
//programmingBook = book;
/**
* 초과 프로퍼티 검사
*/
let book2: Book = {
name: "한 입 크기고 잘라먹는 리액트",
price: 33000,
//skill: "reactjs",
};
let book3: Book = programmingBook;
function func(book: Book) {}
func({
name: "한 입 크기고 잘라먹는 리액트",
price: 33000,
//skill: "reactjs",
});
func(programmingBook);
4. 대수 타입
/**
* 대수 타입
* -> 여러개의 타입을 합성해서 새롭게 만들어낸 타입
* -> 합집합 타입과 교집합 타입이 존재합니다
*/
/**
*1. 합집합 - Union 타입
*/
let a: string | number | boolean | undefined | null | {};
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 타입
*/
let variable: number & string;
type Intersection = Dog & Person;
let Intersection: Intersection = {
name: "",
color: "",
language: "",
};
5. 타입 추론
let으로 타입 지정 안하고 변수 선언 -> number, string 등과 같은 범위로 타입을 추론해준다.
const 로 타입을 지정하면 해당 값이 리터럴 타입으로 들어간다. ex) const a = 10 -> a : 10
/**
* 타입 추론
*/
let a = 10; //number 타입으로 추론 (범용적) = 타입 넓히기
let b = "hello";
let c = {
id: 1,
name: "홍길동",
profile: {
nickname: "winterlood",
},
urls : {"https://winterlood.com"},
};
let { id, name , profile} = c;
let [one, two, three] = [1, "hello", true];
function func(message="hello") {
return "hello";
}
let d; //타입을 정해주지 않으면 암묵적인 any 타입으로 추론됨
// d:any 라고 정의해주는 것과 다름 (이때는 진화 안함)
d = 10; //any -> number 타입으로 변경됨 (=진화 o)
d.toFixed();
d="hello"; //string 타입으로 변경됨
d.toUpperCase();
//d.toFixed();
const num = 10; //상수는 리터럴 타입(10)으로 정의됨
const str = "hello";
//배열은 모든 요소를 확인하여 공통된 타입으로 추론함
let arr = [1, "string"]; //배열 : string | number로 추론됨
6. 타입 단언
/**
* 타입 단언 type assertion
*/
type Person = {
name: string;
age: number;
};
//타입 단언
let person: any = {} as Person; //키워드로 타입을 명시해준다 (Person타입으로 간주하도록)
person.name = "홍길동";
person.age = 27;
type Dog = {
name: string;
color: string;
};
let dog = {
name: "돌돌이",
color: "brown",
breed: "진도", // Dog타입으로 단언하여 해당 프로퍼티가 오류가 나지 않음
} as Dog;
/**
* 타입 단언의 규칙
* 값 as 단언 <- 단언식
* A as B
* A가 B의 슈퍼타입이거나
* A가 B의 서브타입이어야 함
*/
let num1 = 10 as never; //A(10)이 B(never:모든 타입의 서브타입)의 슈퍼타입
let num2 = 10 as unknown; //A(10)가 B(unknown)의 서브타입
//A와 B는 교집합이 없는 타입 -> 에
//number -> unknown -> string 은 가능 : 다중 단언, 지양해야 함
let num3 = 10 as unknown as string;
/**
* const 단언
*/
let num4 = 10 as const; //num : 10로 추론 -> const로 정의한 것과 같이 추론함
let cat = {
name: "야옹이",
color: "yellow",
} as const; //모든 프로퍼티를 readonly로
// cat.name = "";
/**
* Non Null 단언
*/
type Post = {
title: string;
author?: string;
};
let post: Post = {
title: "게시글1",
author: "홍길동",
};
//null or undefined 일 경우 대비로 ? 사용(옵셔널 체이닝)
//number | undefined 형식은 number 에 할당 불가
//? -> ! 로 변경 : null 또는 undefined 가 아닐 것이라고 알림 -> number 타입 할당 가능
const len: number = post.author!.length;
7. 타입 좁히기
/**
* 타입 좁히기
* 조건문 등을 이용해 넓은 타입에서 좁은 타입으로
* 타입을 상황에 따라 좁히는 방법을 이야기하기
*/
type Person = {
name: string;
age: number;
};
//value => number : toFixed
//value => string : toUpperCase
//value => Date : getTime
//value => Person : name은 age살 입니다.
function func(value: number | string | Date | null | Person) {
if (typeof value === "number") {
//number 타입으로 보장됨
console.log(value.toFixed());
} else if (typeof value === "string") {
//string 타입으로 보장됨
console.log(value.toUpperCase());
} else if (value instanceof Date) {
//typeof value === "object" => null도 이 조건을 통과하므로
//아래 코드에서 에러가 발생한다.
console.log(value.getTime());
} else if (value && "age" in value) {
//person은 클래스가 아니므로 instanceof 사용 불가
console.log(`${value.name}은 ${value.age}살 입니다`);
}
}
8. 서로소 유니온 타입
/**
* 서로소 유니온 타입
* 교집합이 없는 타입들로만 만든 유니온 타입을 말함
*/
type Admin = {
tag: "ADMIN";
name: string;
kickCount: number;
};
type Member = {
tag: "MEMBER";
name: string;
point: number;
};
type Guest = {
tag: "GUEST";
name: string;
visitCount: number;
};
//tag : "ADMIN" 과 tag:"MEMBER" 이 다르므로 Admin 과 Member 타입 간의 교집합은 존재 X => 서로소
//서로소 Union 타입
type User = Admin | Member | Guest;
//Admin -> {name}님 현재까지 {kickCount}명 강퇴했습니다.
//Member -> {name}님 현재까지 {point} 모았습니다.
//Guest -> {name}님 현재까지 {visitCount}번 오셨습니다.
function login(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;
}
}
// 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}번 방문하셨습니다.`);
// }
}
/**
* 복습겸 한가지 더 사례
*/
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 = {
tate ="FAILED",
error :{
message : "오류 발생 원인은 ~~~",
},
};
const success : AsyncTask = {
state : "SUCCESS",
response : {
data : "데이터 ~~",
},
}