상품 사이즈 처럼 값의 종류를 나열할 수 있는 타입
enum Size {
S,
M,
L,
XL,
}
let product: {
id: string;
name: string;
price: number;
membersOnly?: boolean;
sizes: Size[];
} = {
id: 'c001',
name: '코드잇 블랙 후디',
price: 129000,
sizes: [Size.M, Size.L],
};
실제로는 0부터 시작하는 정수이다.
if문 같은 것을 쓸 때 0이 false 처리될 수 있으므로 값을 정해놓고 쓰는 것이 좋다.
=을 쓰면 값을 정할 수 있다.
enum Size {
S='S',
M='M',
L='L',
XL='XL',
}
let product: {
id: string;
name: string;
price: number;
membersOnly?: boolean;
sizes: Size[];
} = {
id: 'c001',
name: '코드잇 블랙 후디',
price: 129000,
sizes: [Size.M, Size.L],
};
똑같은 타입을 여러번 정의하는 것은 유지보수하기에 좋은 코드는 아니다. 이럴 때 Interface를 사용한다.
interface Product {
id: string;
name: string;
price: number;
membersOnly?: boolean;
}
const product1: Product = {
id: 'c001',
name: '코드잇 블랙 후디',
price: 129000,
};
const product2: Product = {
id: 'c002',
name: '코드잇 그레이 후디',
price: 179000,
};
상속이 가능하다. (extends 뒤에 부모 Interface를 적어주면 된다.)
interface ClothingProduct extends Product {
size: Size[];
}
const product1: ClothingProduct = {
id: 'c001',
name: '코드잇 블랙 후디',
price: 129000,
};
//함수의 타입도 정의할 수 있다.
interface PrintProductFunction {
(product: Product): void;
}
타입스크립트에서는 변수를 지정할 때 let을 쓰느냐, const를 쓰느냐에 따라서 타입이 다르게 지정된다.
let productName1 = '블랙 티셔츠';//string
const productName2 = '텀블러';//문자열이 타입으로 들어가 있다.
리터럴 : 변수의 값을 곧 타입으로, const는 값이 바뀔 일이 없으니까 아예 변수의 값을 타입으로 추론한 것이다.
문자 리터럴 타입은 문자형 타입에 포함되고 숫자 리터럴 타입은 숫자형 타입에 포함된다.
숫자형 => 숫자 리터럴 타입 인정 불가
숫자 리터럴 => 숫자형 타입 인정 가능
타입도 변수처럼 이름을 붙여줄 수 있다.
모든 타입에 사용할 수 있다.(함수나 객체에도 가능)
객체에서는 타입 별칭도 가능하지만 interface를 사용하는 것이 더 좋다.
type Cart = string[];
type CartResultCallback = (result: boolean) => void;
interface Product = {
id: string;
name: string;
}
const cart: Cart = [
'c001',
'c001',
'c002',
];
interface User {
username: string;
email: string;
cart: Cart;
}
const user: User = {
username: 'codeit',
email: 'typescript@codeit.kr',
cart,
}
자바스크립트의 OR 연산자처럼 A이거나 B이다. 라는 의미의 타입이다.
|
를 사용해서 나타낸다.
만약 공통된 타입이 아닌 특정 타입만 처리하도록 하려면 if in
문을 사용해서 처리한다.
enum ClothingSize {
S = 'S',
M = 'M',
L = 'L',
XL = 'XL',
}
interface Product {
id: string;
name: string;
price: number;
membersOnly?: boolean;
}
interface ClothingProduct extends Product {
sizes: ClothingSize[];
color: string;
}
interface ShoeProduct extends Product {
sizes: number[];
handmade: boolean;
}
function printSizes(product: ClothingProduct | ShoeProduct) {
const availableSizes = product.sizes.join(', ');
console.log(`구매 가능한 사이즈는 다음과 같습니다: ${availableSizes}`); //공통된 프로퍼티만 참조할 수 있다.
if ('color' in product) {
console.log(`색상: ${product.color}`};
} //특정 타입만 처리하도록 할 수 있다.
}
const product1: ClothingProduct = {
id: 'c001',
name: '코드잇 블랙 후드 집업',
price: 129000,
membersOnly: true,
sizes: [ClothingSize.M, ClothingSize.L],
color: 'black',
};
const product2: ShoeProduct = {
id: 's001',
name: '코드잇 스니커즈',
price: 69000,
membersOnly: false,
sizes: [220, 230, 240, 260, 280],
handmade: false,
};
printSizes(product1);
printSizes(product2);
type을 합치는 것
A와 B의 성질을 모두 갖는 타입을 만들고 싶을 때
&도 |와 마찬가지로 여러번 쓸 수 있다
interface Id {
id: string;
}
interface Timestamp {
createdAt: Date;
updatedAt: Date;
}
type Product = Id & {
name: string;
price: number;
membersOnly?: boolean;
}
type User = Id & Timestamp &{
username: string;
email: string;
}
interface Review = Id & Timestamp &{
productId: string;
userId: string;
content: string;
}
const product: Product = {
id: 'c001',
name: '코드잇 블랙 후드티',
price: 129000,
}
const user: User = {
id: 'user0001',
username: 'codeit',
email: 'typescript@codeit.kr',
createdAt: new Date(),
updatedAt: new Date(),
}
const review: Review = {
id: 'review001',
userId: user.id,
productId: product.id,
content: '아주 좋음',
createdAt: new Date(),
updatedAt: new Date(),
}
interface Product {
id: string;
name: string;
price: number;
membersOnly?: boolean;
}
type ProductProperty = keyof Product;//Product 타입에 변화가 생겨도 매번 바꿔주지 않아도 된다.
const productTableKeys: ProductProperty[] = ['name', 'price', 'membersOnly'];
const productTableKeys: (keyof Product)[] = ['name', 'price', 'membersOnly'];//이렇게 바로 사용할 수도 있다
const product: Product = {
id: 'c001',
name: '코드잇 블랙 후드 집업',
price: 129000,
membersOnly: true,
};
for (let key of productTableKeys) {
console.log(`${key} | ${product[key]}`);
}
let product2 : typeof product; // 결과가 타입스크팁크 타입으로 나온다.
console.log(typeof product); //자바스크립트 결과이므로 결과가 문자열이 된다.
객체 타입에서 프로퍼티 이름들을 모아서 Union한 타입으로 만들고 싶을 때 사용
interface Product {
id: string;
name: string;
price: number;
membersOnly?: boolean;
}
type ProductProperty = keyof Product; // 'id' | 'name' | 'price' | 'membersOnly';
typeof 이미 존재하는 타입을 가져와서 타입을 정리할때 사용한다.
자바스크립트 코드에서 사용하면 결괏값이 문자열이지만, 타입스크립트 코드에서 쓸 때는 결과 값은 타입스크립트의 타입
const product: Product = {
id: 'c001',
name: '코드잇 블랙 후드 집업',
price: 129000,
salePrice: 98000,
membersOnly: true,
};
console.log(typeof product); // 문자열 'object'
const product2: typeof product = { // 타입스크립트의 Product 타입
id: 'g001',
name: '코드잇 텀블러',
price: 25000,
salePrice: 19000,
membersOnly: false,
};
아래 두 코드는 거의 같은 역할을 하는 코드이다.
enum UserType {
Admin = 'admin',
User = 'user',
Guest = 'guest',
}
const role = UserType.Admin;
console.log(role === UserType.Guest);
type UserType = 'admin' | 'user' | 'guest'
const role: UserType = 'admin';
console.log(role === 'guest');
두 코드의 차이는 다음과 같다.
우선 Enum 코드를 자바스크립트로 트랜스파일링해 보면 다음과 같다.
"use strict";
var UserType;
(function (UserType) {
UserType["Admin"] = "admin";
UserType["User"] = "user";
UserType["Guest"] = "guest";
})(UserType || (UserType = {}));
const role = UserType.Admin;
console.log(role === UserType.Guest);
Enum은 별도의 자바스크립트 객체를 만들어서 그 객체를 사용한다. UserType은 자바스크립트에서 아래와 같은 객체이다.
{ Admin: 'admin', User: 'user', Guest: 'guest' }
예를 들어서 가능한 UserType 값들을 모두 활용해서 어떤 동작을 구현하고 싶다면 Enum을 써서 Object.keys()라는 함수를 사용해 볼 수 있다.
console.log(Object.keys(UserType)); // ['Admin', 'User', 'Guest']
반면에 타입 별칭은 타입스크립트에서만 의미 있는 코드이다. 그래서 Enum과 달리 자바스크립트로 트랜스파일 했을 때 추가로 객체 같은 걸 만들지 않고 단순히 값만 사용하는 코드가 만들어진다.
"use strict";
const role = 'admin';
console.log(role === 'guest');
대부분의 경우 Enum 또는 타입 별칭을 모두 사용할 수 있지만 되도록 Enum의 목적에 맞는 경우라면 Enum 문법을 사용하는 걸 권장
아래 두 코드는 거의 같은 역할을 하는 코드이다.
interface Entity {
id: string;
createdAt: Date;
updatedAt: Date;
}
interface User extends Entity {
username: string;
email: string;
}
type Entity = {
id: string;
createdAt: Date;
updatedAt: Date;
}
type User = Entity & {
username: string;
email: string;
}
Interface의 상속과 Intersection의 가장 큰 차이점은 Intersection은 두 가지 이상의 타입을 한 번에 합칠 수 있다는 것인데, 이것도 Interface로 아주 불가능하지는 않다.
interface Entity {
id: string;
}
interface TimestampEntity extends Entity {
createdAt: Date;
updatedAt: Date;
}
interface User extends TimestampEntity {
username: string;
email: string;
}
type Id = {
id: string;
}
type Entity = {
createdAt: Date;
updatedAt: Date;
}
type User = Id & Entity & {
username: string;
email: string;
}
대부분의 경우 Interface와 타입 별칭을 둘 다 사용할 수 있다. 하지만 되도록 Interface의 목적에 맞는 경우라면 Interface를 사용하는 걸 권장
타입 별칭은 타입에 '이름'을 정하는 문법이다. 복잡한 타입을 만들고, 그 타입을 여러 곳에서 활용할 때 사용하면 된다. 예를 들자면 아래처럼 복잡한 타입을 만들고 여러 곳에서 재활용할 수 있다.
type Point = [number, number];
type SearchQuery = string | string[];
type Result = SuccessResult | FailedResult;
type Coupon =
| PromotionCoupon
| EmployeeCoupon
| WelcomCoupon
| RewardCoupon
;