이 글은 TypeScript의 기초 개념을 다룬 이전 포스팅에 이어, TypeScript의 심화 개념들을 설명하고자 합니다. 특히, 인터페이스 병합, 제네릭, 타입 가드 등 중요한 TypeScript 기능들을 이해하고 실습할 수 있도록 구성했습니다. 이 내용을 통해 TypeScript의 심화 개념을 쉽게 이해하고 실전에서 활용할 수 있기를 바랍니다.

설명: TypeScript에서는 동일한 이름의 인터페이스가 여러 번 선언되면, 이를 자동으로 병합합니다. 이를 통해 인터페이스를 확장하거나 필요한 속성을 추가할 수 있습니다.
interface IUser {
name: string;
age: number;
}
interface IUser {
email: string;
}
const auser: IUser = {
name: "John",
age: 20,
email: "John@gmail.com",
};
이 코드에서 두 번 선언된 IUser 인터페이스가 병합되어 name, age, email 모두를 포함하게 됩니다. 이를 통해 코드의 확장성을 자연스럽게 확보할 수 있습니다.
인터페이스를 사용해 함수의 타입을 정의할 수 있습니다. 이를 통해 함수의 매개변수와 반환값의 타입을 명확히 할 수 있습니다.
interface IAdd {
(n1: number, n2: number): number;
}
const aDd: IAdd = (n1, n2) => n1 + n2;
이러한 방식으로 함수의 구조를 명확히 정의하면, 코드의 일관성을 유지할 수 있고, 타입 안전성도 보장됩니다.
인덱스 시그니처를 사용하면 객체의 키와 값의 타입을 정의할 수 있습니다. 이 기능은 동적으로 속성이 추가될 수 있는 객체에 유용합니다.
interface ICar {
[key: string]: string | number;
}
const car: ICar = {
name: "Benz",
color: "black",
price: 1000,
};
이 코드에서는 ICar 인터페이스가 키를 문자열로 받고, 값은 문자열이나 숫자일 수 있도록 정의됩니다.
TypeScript는 초기값을 기준으로 변수의 타입을 자동으로 추론합니다. 이를 통해 타입을 명시하지 않아도 안전하게 코드를 작성할 수 있습니다.
let numd = 10;
numd = 20; // number로 자동 추론
let str = "A";
str = "B"; // string으로 자동 추론
타입 추론은 코드의 가독성을 높여주고, 불필요한 타입 선언을 줄여줍니다.
type 키워드를 사용해 타입을 별칭으로 정의할 수 있습니다. 이는 인터페이스와 유사하지만, 더 복잡한 타입이나 유니언 타입 등을 정의할 때 유용합니다.
type TUser = {
name: string;
age: number;
};
type Tjob = { job: string };
type TUserAndJob = TUser & Tjob; // 타입 병합
const user2: TUser = {
name: "Neo",
age: 85,
};
타입 별칭은 코드의 재사용성을 높이고, 복잡한 타입 구조를 간결하게 표현할 수 있습니다.
enum은 관련된 상수들의 집합을 정의하는 데 사용됩니다. 숫자형과 문자형 열거형을 모두 지원하며, 명확한 값의 집합을 표현할 때 유용합니다.
enum Role {
Admin = "admin",
User = "user",
Guest = "guest",
}
const user: IUser = {
name: "Neo",
role: Role.User,
};
열거형을 사용하면 상수를 코드에서 직접 사용하는 대신 의미 있는 이름으로 관리할 수 있어 코드의 가독성이 향상됩니다.
제네릭은 다양한 타입에 대해 동작할 수 있는 함수를 작성할 수 있게 해줍니다. 이를 통해 코드의 재사용성을 극대화할 수 있습니다.
function getSize<T>(arr: T[]): number {
return arr.length;
}
console.log(getSize<number>([1, 2, 3])); // 3
console.log(getSize<string>(["a", "b", "c"])); // 3
제네릭은 함수나 클래스에서 다양한 타입을 유연하게 다룰 수 있게 해주며, 코드의 재사용성을 높여줍니다.
타입 가드는 변수의 타입을 좁혀 특정 타입에 대해 안전하게 작업할 수 있게 합니다.
const getRetunValue = (n: number | string) => {
if (typeof n === "number") return n.toFixed(1);
else return n;
};
타입 가드를 사용하면 복합 타입에서 안전하게 작업할 수 있어 런타임 오류를 줄일 수 있습니다.
이 예제는 TypeScript의 다양한 개념을 종합적으로 활용한 예제입니다. 인터페이스로 Todo 아이템의 구조를 정의하고, CRUD 작업을 수행합니다.
interface Todo {
id: number;
text: string;
completed: boolean;
}
const todos: Todo[] = [];
const addTodo = (text: string): void => {
const todo: Todo = {
id: todos.length + 1,
text,
completed: false,
};
todos.push(todo);
};
const removeTodo = (id: number): void => {
const findIndex = todos.findIndex((todo) => todo.id === id);
if (findIndex !== -1) {
todos.splice(findIndex, 1);
}
};
const toggleTodo = (id: number): void => {
const find = todos.find((todo) => todo.id === id);
if (find) {
find.completed = !find.completed;
}
};
이 Todo 리스트 예제에서는 TypeScript의 다양한 기능을 실전에서 어떻게 활용할 수 있는지 배울 수 있습니다. 인터페이스를 통해 객체의 구조를 명확히 하고, 제네릭과 타입 가드 등을 통해 코드의 안정성과 재사용성을 극대화할 수 있었습니다.

참말로 어질어질하죠잉?
이번 글에서는 TypeScript의 심화 개념을 다루었습니다. 인터페이스 병합, 제네릭, 타입 가드 등 TypeScript의 강력한 기능을 통해 코드의 안정성과 재사용성을 크게 높일 수 있었습니다. TypeScript는 복잡한 애플리케이션에서도 타입 안전성을 보장하면서 효율적인 코드를 작성할 수 있게 해주는 매우 유용한 도구입니다. 이 글을 통해 TypeScript의 심화 개념을 이해하고, 실전 프로젝트에 적용해보시길 바랍니다.