WeniVooks

검색

타입스크립트 베이스캠프

객체와 타입 정의

1. 객체와 타입 정의

1.1 객체와 타입스크립트

타입스크립트에서 {}를 통해 객체를 생성하면 새로운 타입이 생성됩니다.

const obj = {
  key: 'test',
};
const obj = {
  key: 'test',
};

이때 obj의 타입은 { key: string }으로 추론됩니다. 명시적으로 타입을 정의할 수도 있습니다.

let obj: { key: string };
let obj: { key: string };
1.2 type 키워드

타입 별칭(Type Alias)을 사용하면 반복되는 타입 정의를 재사용할 수 있습니다.

// Todo 타입 정의
type Todo = {
  key: string;
  todo: string;
};
 
// 타입 사용하기
function addTodo(newTodoData: Todo) {
  // Todo 처리 로직
}
 
function editTodo(prevTodo: Todo): Todo {
  // Todo 수정 로직
  return editedTodo;
}
// Todo 타입 정의
type Todo = {
  key: string;
  todo: string;
};
 
// 타입 사용하기
function addTodo(newTodoData: Todo) {
  // Todo 처리 로직
}
 
function editTodo(prevTodo: Todo): Todo {
  // Todo 수정 로직
  return editedTodo;
}

아래와 같이 type안에 type을 중첩하여 사용할 수도 있습니다.

type author = {
    name: string;
    email: string;
};
 
type notice = {
    auth: author;
    id: number;
    title: string;
    content: string;
};
 
const noticeData = {
    auth: {
        name: "licat",
        email: "licat@weniv.co.kr"
    },
    id: 1,
    title: "공지사항",
    content: "안녕하세요. TypeScript 공부중입니다."
};
 
function printNoticeData(noticeData: notice) {
    console.log(noticeData);
}
 
printNoticeData(noticeData);
type author = {
    name: string;
    email: string;
};
 
type notice = {
    auth: author;
    id: number;
    title: string;
    content: string;
};
 
const noticeData = {
    auth: {
        name: "licat",
        email: "licat@weniv.co.kr"
    },
    id: 1,
    title: "공지사항",
    content: "안녕하세요. TypeScript 공부중입니다."
};
 
function printNoticeData(noticeData: notice) {
    console.log(noticeData);
}
 
printNoticeData(noticeData);
1.2.1 선택적 속성

?를 이용하여 선택적 속성을 정의할 수 있습니다.

type Todo = {
  key: string;
  todo: string;
  caption?: string; // 선택적 속성
};
type Todo = {
  key: string;
  todo: string;
  caption?: string; // 선택적 속성
};
1.2.2 타입 별칭의 다양한 사용

타입 별칭은 객체 타입뿐만 아니라 다른 타입에도 사용할 수 있습니다.

type Id = string;
type NumberOrString = number | string;
type Id = string;
type NumberOrString = number | string;
1.2.3 연산자를 이용한 타입 정의

|& 연산자를 이용하여 유니온 타입과 인터섹션 타입을 정의할 수 있습니다.

  1. 유니온 타입(|): "or"

    • A | B는 "A 타입 또는 B 타입"이라는 의미입니다.
    • 예: string | number는 "문자열 또는 숫자"
  2. 인터섹션 타입(&): "and"

    • A & B는 "A 타입과 B 타입 모두"라는 의미입니다.
    • 예: Character & Pet은 "Character의 모든 속성과 Pet의 모든 속성을 다 가진 타입"
// 1. 유니온 타입 예제
type Score = number | string;  // 점수는 숫자나 문자열로 표현할 수 있음
 
function printScore(score: Score) {
    console.log(`점수: ${score}`);
}
 
printScore(100);      // OK
printScore("A+");     // OK
printScore(true);     // 에러! boolean은 Score 타입이 아님
 
// 2. 인터섹션 타입 예제
type Student = {
    name: string;
    grade: number;
};
 
type Athlete = {
    sport: string;
    level: number;
};
 
// 학생이면서 운동선수인 타입
type StudentAthlete = Student & Athlete;
 
const kim: StudentAthlete = {
    name: "김철수",    // Student 타입에서 필요
    grade: 3,         // Student 타입에서 필요
    sport: "축구",     // Athlete 타입에서 필요
    level: 5          // Athlete 타입에서 필요
};
// 1. 유니온 타입 예제
type Score = number | string;  // 점수는 숫자나 문자열로 표현할 수 있음
 
function printScore(score: Score) {
    console.log(`점수: ${score}`);
}
 
printScore(100);      // OK
printScore("A+");     // OK
printScore(true);     // 에러! boolean은 Score 타입이 아님
 
// 2. 인터섹션 타입 예제
type Student = {
    name: string;
    grade: number;
};
 
type Athlete = {
    sport: string;
    level: number;
};
 
// 학생이면서 운동선수인 타입
type StudentAthlete = Student & Athlete;
 
const kim: StudentAthlete = {
    name: "김철수",    // Student 타입에서 필요
    grade: 3,         // Student 타입에서 필요
    sport: "축구",     // Athlete 타입에서 필요
    level: 5          // Athlete 타입에서 필요
};

실제로 많이 사용되는 경우는 다음과 같습니다.

  • 유니온(|): 여러 타입 중 하나를 선택할 때 (예: 숫자나 문자열로 된 ID)
  • 인터섹션(&): 여러 타입의 특성을 모두 합칠 때 (예: 회원이면서 관리자인 사용자)

아래처럼 함수에 프로퍼티를 추가하는 형태로도 사용할 수 있습니다.

type Student = {
    name: string;
    grade: number;
};
 
type Athlete = {
    sport: string;
    level: number;
};
 
// 학생이면서 운동선수인 타입
function printStudentAthlete(student: Student, athlete: Athlete) {
    console.log(student);
    console.log(athlete);
}
 
function printStudentAthlete2(student: Student & Athlete) {
    console.log(student);
}
 
printStudentAthlete({ name: 'licat', grade: 3 }, { sport: 'soccer', level: 5 });
printStudentAthlete2({ name: 'licat', grade: 3, sport: 'soccer', level: 5 });
type Student = {
    name: string;
    grade: number;
};
 
type Athlete = {
    sport: string;
    level: number;
};
 
// 학생이면서 운동선수인 타입
function printStudentAthlete(student: Student, athlete: Athlete) {
    console.log(student);
    console.log(athlete);
}
 
function printStudentAthlete2(student: Student & Athlete) {
    console.log(student);
}
 
printStudentAthlete({ name: 'licat', grade: 3 }, { sport: 'soccer', level: 5 });
printStudentAthlete2({ name: 'licat', grade: 3, sport: 'soccer', level: 5 });
1.2.4 읽기전용 속성

readonly 키워드로 읽기전용 속성을 정의할 수 있습니다.

type Todo = {
    readonly key: string;
    name: string;
};
 
const todo: Todo = {
    key: "1",
    name: "할 일 1"
};
 
// todo.key = "2"; // 에러! 읽기 전용 속성이므로 재할당 불가
todo.name = "할 일 2"; // OK
console.log(todo);
type Todo = {
    readonly key: string;
    name: string;
};
 
const todo: Todo = {
    key: "1",
    name: "할 일 1"
};
 
// todo.key = "2"; // 에러! 읽기 전용 속성이므로 재할당 불가
todo.name = "할 일 2"; // OK
console.log(todo);
1.2.5 구조적 타이핑

타입스크립트는 타입의 구조가 호환되면 같은 타입으로 취급합니다.

type Todo = { todo: string };
type TodoWithKey = { key: string; todo: string };
 
function processTodo(todo: Todo) {
  console.log(todo.todo);
}
 
const extendedTodo: TodoWithKey = { key: '1', todo: '할 일' };
processTodo(extendedTodo); // 정상 작동
type Todo = { todo: string };
type TodoWithKey = { key: string; todo: string };
 
function processTodo(todo: Todo) {
  console.log(todo.todo);
}
 
const extendedTodo: TodoWithKey = { key: '1', todo: '할 일' };
processTodo(extendedTodo); // 정상 작동

여기서 주의해야 할 점은 타입스크립트는 타입 호환성을 체크할 때 객체가 필요한 프로퍼티들을 "최소한" 가지고 있는지만 확인한다는 것입니다. 추가적인 프로퍼티가 있어도 문제되지 않습니다. 지금 todo 변수는 key도 가지고 있는 상태입니다. 쉽게 말해, "필요한 프로퍼티를 가지고 있다면, 그 타입으로 취급한다"라고 생각하면 됩니다.

type Person = {
    name: string;
    age: number;
};
 
type Developer = {
    name: string;
    age: number;
    skills: string[];
};
 
let person: Person = { name: "김철수", age: 20 };
let developer: Developer = { name: "김영희", age: 25, skills: ["JavaScript", "TypeScript"] };
 
person = developer; // OK
console.log(person);
type Person = {
    name: string;
    age: number;
};
 
type Developer = {
    name: string;
    age: number;
    skills: string[];
};
 
let person: Person = { name: "김철수", age: 20 };
let developer: Developer = { name: "김영희", age: 25, skills: ["JavaScript", "TypeScript"] };
 
person = developer; // OK
console.log(person);

2. 연습문제

  1. 다음 조건을 만족하는 타입을 정의해보세요.

    • 사용자 정보를 담는 User 타입
    • 필수 속성: id(숫자), name(문자열)
    • 선택적 속성: email(문자열)
    • id는 읽기전용으로 설정
  2. 다음 두 타입을 정의하고 인터섹션 타입을 만들어보세요.

    • Product: name(문자열), price(숫자) 속성
    • Discount: discountRate(숫자) 속성
    • DiscountedProduct: 위 두 타입의 인터섹션

3. 연습문제 정답

  1. 다음과 같이 User 타입을 정의할 수 있습니다.
// User 타입 정의
type User = {
    readonly id: number;
    name: string;
    email?: string;
};
 
// User 정보를 출력하는 함수
function printUserInfo(user: User): void {
    console.log("=== User Information ===");
    console.log(`ID: ${user.id}`);
    console.log(`Name: ${user.name}`);
    if (user.email) {
        console.log(`Email: ${user.email}`);
    }
}
 
// 사용 예시
const user1: User = {
    id: 1,
    name: "Kim"
};
 
const user2: User = {
    id: 2,
    name: "Lee",
    email: "lee@example.com"
};
 
printUserInfo(user1);  // email 없는 경우
printUserInfo(user2);  // email 있는 경우
// User 타입 정의
type User = {
    readonly id: number;
    name: string;
    email?: string;
};
 
// User 정보를 출력하는 함수
function printUserInfo(user: User): void {
    console.log("=== User Information ===");
    console.log(`ID: ${user.id}`);
    console.log(`Name: ${user.name}`);
    if (user.email) {
        console.log(`Email: ${user.email}`);
    }
}
 
// 사용 예시
const user1: User = {
    id: 1,
    name: "Kim"
};
 
const user2: User = {
    id: 2,
    name: "Lee",
    email: "lee@example.com"
};
 
printUserInfo(user1);  // email 없는 경우
printUserInfo(user2);  // email 있는 경우
  1. 다음과 같이 Product, Discount, DiscountedProduct 타입을 정의할 수 있습니다.
// Product 타입 정의
type Product = {
    name: string;
    price: number;
};
 
// Discount 타입 정의
type Discount = {
    discountRate: number;
};
 
// Product와 Discount의 인터섹션 타입
type DiscountedProduct = Product & Discount;
 
// 할인된 상품 정보를 출력하는 함수
function printDiscountedProductInfo(product: DiscountedProduct): void {
    const discountedPrice = product.price * (1 - product.discountRate);
    
    console.log("=== Discounted Product Information ===");
    console.log(`Name: ${product.name}`);
    console.log(`Original Price: ${product.price.toLocaleString()}원`);
    console.log(`Discount Rate: ${product.discountRate * 100}%`);
    console.log(`Final Price: ${discountedPrice.toLocaleString()}원`);
}
 
// 사용 예시
const product: DiscountedProduct = {
    name: "Laptop",
    price: 1000000,
    discountRate: 0.1
};
 
printDiscountedProductInfo(product);
// Product 타입 정의
type Product = {
    name: string;
    price: number;
};
 
// Discount 타입 정의
type Discount = {
    discountRate: number;
};
 
// Product와 Discount의 인터섹션 타입
type DiscountedProduct = Product & Discount;
 
// 할인된 상품 정보를 출력하는 함수
function printDiscountedProductInfo(product: DiscountedProduct): void {
    const discountedPrice = product.price * (1 - product.discountRate);
    
    console.log("=== Discounted Product Information ===");
    console.log(`Name: ${product.name}`);
    console.log(`Original Price: ${product.price.toLocaleString()}원`);
    console.log(`Discount Rate: ${product.discountRate * 100}%`);
    console.log(`Final Price: ${discountedPrice.toLocaleString()}원`);
}
 
// 사용 예시
const product: DiscountedProduct = {
    name: "Laptop",
    price: 1000000,
    discountRate: 0.1
};
 
printDiscountedProductInfo(product);
2.2 함수와 타입2.4 인터페이스