10. TypeScript κΈ°μ΄ (TypeScript Fundamentals)
10. TypeScript κΈ°μ΄ (TypeScript Fundamentals)¶
νμ΅ λͺ©ν¶
- TypeScriptμ μ₯μ κ³Ό JavaScriptμμ κ΄κ³ μ΄ν΄
- κΈ°λ³Έ νμ μμ€ν λ§μ€ν°
- μΈν°νμ΄μ€μ νμ λ³μΉ νμ©
- μ λ€λ¦μ ν΅ν μ¬μ¬μ© κ°λ₯ν μ½λ μμ±
- μ νΈλ¦¬ν° νμ κ³Ό κ³ κΈ νμ κΈ°λ₯ μ΄ν΄
λͺ©μ°¨¶
- TypeScript μκ°
- κΈ°λ³Έ νμ
- μΈν°νμ΄μ€μ νμ
- ν¨μ νμ
- μ λ€λ¦
- μ νΈλ¦¬ν° νμ
- μ°μ΅ λ¬Έμ
1. TypeScript μκ°¶
1.1 TypeScriptλ?¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TypeScript κ°μ β
β β
β TypeScript = JavaScript + μ μ νμ
β
β β
β νΉμ§: β
β - Microsoftμμ κ°λ° β
β - JavaScriptμ μμ μ§ν© (Superset) β
β - μ»΄νμΌ μ νμ
κ²μ¬ β
β - λͺ¨λ JavaScript μ½λλ μ ν¨ν TypeScript β
β β
β μ₯μ : β
β - λ°νμ μ μ€λ₯ λ°κ²¬ β
β - IDE μ§μ ν₯μ (μλμμ±, 리ν©ν λ§) β
β - μ½λ κ°λ
μ± λ° λ¬Έμν β
β - λκ·λͺ¨ νλ‘μ νΈ μ μ§λ³΄μ μ©μ΄ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
1.2 μ€μΉ λ° μ€μ ¶
# TypeScript μ€μΉ
npm install -g typescript
# λ²μ νμΈ
tsc --version
# νλ‘μ νΈ μ΄κΈ°ν
npm init -y
npm install typescript --save-dev
# tsconfig.json μμ±
npx tsc --init
// tsconfig.json κΈ°λ³Έ μ€μ
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"moduleResolution": "node"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
1.3 μ»΄νμΌκ³Ό μ€ν¶
# λ¨μΌ νμΌ μ»΄νμΌ
tsc hello.ts
# νλ‘μ νΈ μ 체 μ»΄νμΌ
tsc
# κ°μ λͺ¨λ (νμΌ λ³κ²½ μ μλ μ»΄νμΌ)
tsc --watch
# ts-nodeλ‘ μ§μ μ€ν (κ°λ°μ©)
npm install -g ts-node
ts-node hello.ts
2. κΈ°λ³Έ νμ ¶
2.1 μμ νμ ¶
// λ¬Έμμ΄
let name: string = "TypeScript";
let greeting: string = `Hello, ${name}!`;
// μ«μ
let age: number = 25;
let price: number = 99.99;
let hex: number = 0xf00d;
// λΆλ¦¬μΈ
let isActive: boolean = true;
let hasError: boolean = false;
// nullκ³Ό undefined
let nothing: null = null;
let notDefined: undefined = undefined;
// BigInt (ES2020+)
let bigNumber: bigint = 9007199254740991n;
// Symbol
let sym: symbol = Symbol("unique");
2.2 λ°°μ΄κ³Ό νν¶
// λ°°μ΄ νμ
(λ κ°μ§ λ°©μ)
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: Array<string> = ["a", "b", "c"];
// λ€μ°¨μ λ°°μ΄
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
];
// νν (κ³ μ κΈΈμ΄, κ³ μ νμ
λ°°μ΄)
let tuple: [string, number] = ["Alice", 30];
let rgb: [number, number, number] = [255, 128, 0];
// νν μμ μ κ·Ό
const [userName, userAge] = tuple;
console.log(userName); // "Alice"
// λͺ
λͺ
λ νν (κ°λ
μ± ν₯μ)
type Point = [x: number, y: number];
const point: Point = [10, 20];
2.3 κ°μ²΄ νμ ¶
// κΈ°λ³Έ κ°μ²΄ νμ
let person: { name: string; age: number } = {
name: "Bob",
age: 25,
};
// μ νμ μμ± (?)
let config: { host: string; port?: number } = {
host: "localhost",
// portλ μ νμ
};
// μ½κΈ° μ μ© μμ±
let user: { readonly id: number; name: string } = {
id: 1,
name: "Alice",
};
// user.id = 2; // μ€λ₯! readonly
// μΈλ±μ€ μκ·Έλμ²
let dictionary: { [key: string]: number } = {
apple: 1,
banana: 2,
};
2.4 νΉμ νμ ¶
// any - λͺ¨λ νμ
νμ© (μ¬μ© μμ )
let anything: any = "hello";
anything = 42;
anything = { foo: "bar" };
// unknown - anyλ³΄λ€ μμ ν λμ
let unknownValue: unknown = "hello";
// unknownValue.toUpperCase(); // μ€λ₯!
if (typeof unknownValue === "string") {
unknownValue.toUpperCase(); // OK - νμ
κ°λ ν
}
// void - λ°νκ° μμ
function logMessage(msg: string): void {
console.log(msg);
}
// never - μ λ λ°ννμ§ μμ
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
2.5 Unionκ³Ό Intersection¶
// Union νμ
(|) - μ¬λ¬ νμ
μ€ νλ
let id: string | number;
id = "abc";
id = 123;
type Status = "pending" | "approved" | "rejected";
let orderStatus: Status = "pending";
// Intersection νμ
(&) - λͺ¨λ νμ
κ²°ν©
type Name = { name: string };
type Age = { age: number };
type Person = Name & Age;
const person: Person = {
name: "Alice",
age: 30,
};
2.6 νμ μΆλ‘ κ³Ό νμ λ¨μΈ¶
// νμ
μΆλ‘ - TypeScriptκ° μλμΌλ‘ νμ
κ²°μ
let message = "Hello"; // stringμΌλ‘ μΆλ‘
let count = 10; // numberλ‘ μΆλ‘
// νμ
λ¨μΈ (Type Assertion)
let someValue: unknown = "this is a string";
// λ°©λ² 1: as λ¬Έλ² (κΆμ₯)
let strLength1: number = (someValue as string).length;
// λ°©λ² 2: angle-bracket λ¬Έλ² (JSXμ μΆ©λ)
let strLength2: number = (<string>someValue).length;
// const λ¨μΈ
let colors = ["red", "green", "blue"] as const;
// readonly ["red", "green", "blue"] νμ
// Non-null λ¨μΈ (!)
function getLength(str: string | null): number {
return str!.length; // nullμ΄ μλμ λ¨μΈ
}
3. μΈν°νμ΄μ€μ νμ ¶
3.1 μΈν°νμ΄μ€ κΈ°λ³Έ¶
// μΈν°νμ΄μ€ μ μ
interface User {
id: number;
name: string;
email: string;
age?: number; // μ νμ
readonly createdAt: Date; // μ½κΈ° μ μ©
}
// μΈν°νμ΄μ€ μ¬μ©
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com",
createdAt: new Date(),
};
// ν¨μ νμ
μΈν°νμ΄μ€
interface Calculator {
(a: number, b: number): number;
}
const add: Calculator = (a, b) => a + b;
3.2 μΈν°νμ΄μ€ νμ₯¶
// μΈν°νμ΄μ€ μμ
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
bark(): void;
}
const myDog: Dog = {
name: "Buddy",
age: 3,
breed: "Labrador",
bark() {
console.log("Woof!");
},
};
// λ€μ€ μμ
interface Pet extends Animal {
owner: string;
}
interface ServiceDog extends Dog, Pet {
certificationId: string;
}
3.3 νμ λ³μΉ (Type Alias)¶
// νμ
λ³μΉ μ μ
type ID = string | number;
type Point = { x: number; y: number };
type Callback = (data: string) => void;
// μ¬μ©
let userId: ID = "user_123";
let position: Point = { x: 10, y: 20 };
// μ λμ¨ νμ
μ μ μ©
type Result<T> = { success: true; data: T } | { success: false; error: string };
function fetchData(): Result<User> {
return { success: true, data: { id: 1, name: "Alice", email: "a@b.com", createdAt: new Date() } };
}
3.4 μΈν°νμ΄μ€ vs νμ ¶
// μΈν°νμ΄μ€ - μ μΈ λ³ν© κ°λ₯
interface Window {
title: string;
}
interface Window {
size: number; // μλ λ³ν©λ¨
}
// νμ
- λ³ν© λΆκ°, λ μ μ°
type StringOrNumber = string | number; // μ λμ¨
type Point = [number, number]; // νν
// κΆμ₯μ¬ν:
// - κ°μ²΄ νν μ μ: interface μ¬μ©
// - μ λμ¨, νν, μμ νμ
λ³μΉ: type μ¬μ©
// - λΌμ΄λΈλ¬λ¦¬ API: interface (νμ₯ κ°λ₯)
4. ν¨μ νμ ¶
4.1 ν¨μ νμ μ μ¶
// ν¨μ μ μΈ
function add(a: number, b: number): number {
return a + b;
}
// νμ΄ν ν¨μ
const multiply = (a: number, b: number): number => a * b;
// ν¨μ νμ
λ³μΉ
type MathOperation = (a: number, b: number) => number;
const divide: MathOperation = (a, b) => a / b;
// ν¨μ νμ
μΈν°νμ΄μ€
interface MathFunc {
(a: number, b: number): number;
description?: string;
}
4.2 λ§€κ°λ³μ μ΅μ ¶
// μ νμ λ§€κ°λ³μ (?)
function greet(name: string, greeting?: string): string {
return `${greeting || "Hello"}, ${name}!`;
}
// κΈ°λ³Έκ° λ§€κ°λ³μ
function greetWithDefault(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
// λλ¨Έμ§ λ§€κ°λ³μ
function sum(...numbers: number[]): number {
return numbers.reduce((acc, n) => acc + n, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
4.3 ν¨μ μ€λ²λ‘λ©¶
// ν¨μ μ€λ²λ‘λ© μκ·Έλμ²
function process(x: string): string;
function process(x: number): number;
function process(x: string | number): string | number {
if (typeof x === "string") {
return x.toUpperCase();
}
return x * 2;
}
console.log(process("hello")); // "HELLO"
console.log(process(5)); // 10
4.4 this νμ ¶
interface Button {
label: string;
click(this: Button): void;
}
const button: Button = {
label: "Submit",
click() {
console.log(`Clicked: ${this.label}`);
},
};
button.click(); // OK
// const handler = button.click;
// handler(); // μ€λ₯! this 컨ν
μ€νΈ μμ€
5. μ λ€λ¦¶
5.1 μ λ€λ¦ κΈ°λ³Έ¶
// μ λ€λ¦ ν¨μ
function identity<T>(arg: T): T {
return arg;
}
// μ¬μ©
let output1 = identity<string>("hello");
let output2 = identity<number>(42);
let output3 = identity("auto"); // νμ
μΆλ‘
// μ λ€λ¦ λ°°μ΄
function firstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const first = firstElement([1, 2, 3]); // number | undefined
5.2 μ λ€λ¦ μΈν°νμ΄μ€μ νμ ¶
// μ λ€λ¦ μΈν°νμ΄μ€
interface Box<T> {
value: T;
}
const stringBox: Box<string> = { value: "hello" };
const numberBox: Box<number> = { value: 42 };
// μ λ€λ¦ νμ
λ³μΉ
type Result<T> = {
success: boolean;
data: T;
};
type Pair<K, V> = {
key: K;
value: V;
};
const pair: Pair<string, number> = { key: "age", value: 30 };
5.3 μ λ€λ¦ μ μ½μ‘°κ±΄¶
// extendsλ‘ μ μ½ μΆκ°
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("hello"); // OK - string has length
logLength([1, 2, 3]); // OK - array has length
// logLength(123); // μ€λ₯! number has no length
// keyof μ μ½μ‘°κ±΄
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 30 };
const name = getProperty(person, "name"); // string
const age = getProperty(person, "age"); // number
// getProperty(person, "email"); // μ€λ₯!
5.4 μ λ€λ¦ ν΄λμ€¶
class Queue<T> {
private items: T[] = [];
enqueue(item: T): void {
this.items.push(item);
}
dequeue(): T | undefined {
return this.items.shift();
}
peek(): T | undefined {
return this.items[0];
}
get length(): number {
return this.items.length;
}
}
const numberQueue = new Queue<number>();
numberQueue.enqueue(1);
numberQueue.enqueue(2);
console.log(numberQueue.dequeue()); // 1
6. μ νΈλ¦¬ν° νμ ¶
6.1 κΈ°λ³Έ μ νΈλ¦¬ν° νμ ¶
interface User {
id: number;
name: string;
email: string;
age?: number;
}
// Partial<T> - λͺ¨λ μμ± μ νμ μΌλ‘
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; age?: number }
// Required<T> - λͺ¨λ μμ± νμλ‘
type RequiredUser = Required<User>;
// { id: number; name: string; email: string; age: number }
// Readonly<T> - λͺ¨λ μμ± μ½κΈ° μ μ©
type ReadonlyUser = Readonly<User>;
// Pick<T, K> - νΉμ μμ±λ§ μ ν
type UserBasic = Pick<User, "id" | "name">;
// { id: number; name: string }
// Omit<T, K> - νΉμ μμ± μ μΈ
type UserWithoutEmail = Omit<User, "email">;
// { id: number; name: string; age?: number }
6.2 λ μ½λμ λ§΅ν¶
// Record<K, T> - ν€-κ° λ§΅ν
type UserRole = "admin" | "user" | "guest";
type RolePermissions = Record<UserRole, string[]>;
const permissions: RolePermissions = {
admin: ["read", "write", "delete"],
user: ["read", "write"],
guest: ["read"],
};
// μ¬μ© μμ
type PageInfo = {
title: string;
url: string;
};
type Pages = Record<"home" | "about" | "contact", PageInfo>;
6.3 μ‘°κ±΄λΆ νμ ¶
// Exclude<T, U> - Tμμ U μ μΈ
type Numbers = 1 | 2 | 3 | 4 | 5;
type SmallNumbers = Exclude<Numbers, 4 | 5>; // 1 | 2 | 3
// Extract<T, U> - Tμ Uμ κ³΅ν΅ νμ
type Common = Extract<"a" | "b" | "c", "a" | "c" | "d">; // "a" | "c"
// NonNullable<T> - null, undefined μ μΈ
type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>; // string
// ReturnType<T> - ν¨μ λ°ν νμ
function getUser() {
return { id: 1, name: "Alice" };
}
type UserReturn = ReturnType<typeof getUser>;
// { id: number; name: string }
// Parameters<T> - ν¨μ λ§€κ°λ³μ νμ
type UserParams = Parameters<typeof getUser>; // []
6.4 ν νλ¦Ώ 리ν°λ΄ νμ ¶
// λ¬Έμμ΄ λ¦¬ν°λ΄ μ‘°ν©
type Color = "red" | "green" | "blue";
type Size = "small" | "medium" | "large";
type ClassName = `${Size}-${Color}`;
// "small-red" | "small-green" | ... | "large-blue"
// μ΄λ²€νΈ μ΄λ¦ μμ±
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
7. μ°μ΅ λ¬Έμ ¶
μ°μ΅ 1: νμ μ μ¶
λ€μ λ°μ΄ν° ꡬ쑰μ λν νμ μ μ μνμΈμ.
// μμ λ΅μ
interface Product {
id: number;
name: string;
price: number;
category: string;
inStock: boolean;
tags?: string[];
}
interface CartItem {
product: Product;
quantity: number;
}
interface ShoppingCart {
items: CartItem[];
total: number;
couponCode?: string;
}
μ°μ΅ 2: μ λ€λ¦ ν¨μ¶
λ°°μ΄μμ 쑰건μ λ§λ 첫 λ²μ§Έ μμλ₯Ό μ°Ύλ μ λ€λ¦ ν¨μλ₯Ό μμ±νμΈμ.
// μμ λ΅μ
function find<T>(arr: T[], predicate: (item: T) => boolean): T | undefined {
for (const item of arr) {
if (predicate(item)) {
return item;
}
}
return undefined;
}
// μ¬μ©
const numbers = [1, 2, 3, 4, 5];
const firstEven = find(numbers, (n) => n % 2 === 0); // 2
const users = [{ name: "Alice" }, { name: "Bob" }];
const alice = find(users, (u) => u.name === "Alice");
μ°μ΅ 3: μ νΈλ¦¬ν° νμ νμ©¶
API μλ΅ νμ μ μ μνμΈμ.
// μμ λ΅μ
interface ApiResponse<T> {
success: boolean;
data: T;
error?: string;
timestamp: number;
}
type User = {
id: number;
name: string;
email: string;
};
type UserResponse = ApiResponse<User>;
type UsersResponse = ApiResponse<User[]>;
type DeleteResponse = ApiResponse<{ deleted: boolean }>;
// Partialμ νμ©ν μ
λ°μ΄νΈ νμ
type UserUpdate = Partial<Omit<User, "id">>;