๐Ÿ”ท TypeScript ์ž…๋ฌธ: JavaScript ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ํƒ€์ž… ์‹œ์Šคํ…œ ์™„์ „ ๊ฐ€์ด๋“œ

@leekh8 ยท April 20, 2026 ยท 12 min read

JavaScript๋กœ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์ž‘์—…ํ•˜๋‹ค ๋ณด๋ฉด ์–ด๋А ์ˆœ๊ฐ„ ์ด๋Ÿฐ ์ƒํ™ฉ์ด ์˜จ๋‹ค.

function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.pricee, 0); // ์˜คํƒ€์ธ๋ฐ ์˜ค๋ฅ˜ ์—†์Œ
}

pricee๋ผ๋Š” ์˜คํƒ€๊ฐ€ ์žˆ์ง€๋งŒ JavaScript๋Š” ์•„๋ฌด ๊ฒฝ๊ณ ๋„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค. ์‹คํ–‰ํ•˜๋ฉด NaN์ด ๋‚˜์˜ค๊ณ , ๊ทธ๋•Œ์•ผ ๋””๋ฒ„๊น…์„ ์‹œ์ž‘ํ•œ๋‹ค. TypeScript๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์ˆœ๊ฐ„ ์žก์•„์ค€๋‹ค.


์™œ TypeScript์ธ๊ฐ€

JavaScript๊ฐ€ ๋†“์น˜๋Š” ์˜ค๋ฅ˜๋“ค

// โŒ ๋Ÿฐํƒ€์ž„์— ํ„ฐ์ง€๋Š” JavaScript ์ฝ”๋“œ๋“ค

// 1. ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์†์„ฑ ์ ‘๊ทผ
const user = { name: 'Alice', age: 25 };
console.log(user.email.toLowerCase()); // TypeError: Cannot read properties of undefined

// 2. ์ž˜๋ชป๋œ ํƒ€์ž… ์—ฐ์‚ฐ
function double(n) {
  return n * 2;
}
double('3'); // "33" โ€” ๋ฌธ์ž์—ด์ด ๋“ค์–ด์™”๋Š”๋ฐ ์˜ค๋ฅ˜ ์—†์Œ

// 3. ์˜คํƒ€
const CONFIG = { apiUrl: 'https://api.example.com' };
fetch(CONFIG.apiURl); // undefined โ€” ์˜คํƒ€์ธ๋ฐ ์กฐ์šฉํžˆ ์‹คํŒจ

์ด ๋ชจ๋“  ์˜ค๋ฅ˜๋Š” ๋Ÿฐํƒ€์ž„์— ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋งˆ์ฃผ์น˜๊ฒŒ ๋œ๋‹ค.

TypeScript๋ฅผ ์“ฐ๋ฉด ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์ปดํŒŒ์ผ ํƒ€์ž„์— ๋ฐ”๋กœ ์˜ค๋ฅ˜๋กœ ํ‘œ์‹œ๋œ๋‹ค:

// โœ… TypeScript๊ฐ€ ๋ฏธ๋ฆฌ ์žก์•„์ฃผ๋Š” ์˜ค๋ฅ˜๋“ค

const user: { name: string; age: number } = { name: 'Alice', age: 25 };
console.log(user.email.toLowerCase()); // ์˜ค๋ฅ˜: 'email' ์†์„ฑ์ด ์—†์Œ

function double(n: number): number {
  return n * 2;
}
double('3'); // ์˜ค๋ฅ˜: 'string'์€ 'number' ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Œ

TypeScript ์ฑ„ํƒ ํ˜„ํ™ฉ

  • Stack Overflow ๊ฐœ๋ฐœ์ž ์„ค๋ฌธ(2024)์—์„œ TypeScript๋Š” ๊ฐ€์žฅ ์‚ฌ๋ž‘๋ฐ›๋Š” ์–ธ์–ด 4์œ„
  • Angular๋Š” TypeScript ๊ธฐ๋ณธ, React/Vue๋„ TypeScript First ๋ฐฉํ–ฅ์œผ๋กœ ์ „ํ™˜ ์ค‘
  • Microsoft, Google, Airbnb, Slack ๋“ฑ ๋Œ€ํ˜• ํ”„๋กœ์ ํŠธ ๋Œ€๋ถ€๋ถ„ TypeScript ์‚ฌ์šฉ

TypeScript ์„ค์น˜ ๋ฐ ์‹œ์ž‘

์ˆœ์ˆ˜ TypeScript ํ”„๋กœ์ ํŠธ

# TypeScript ์ปดํŒŒ์ผ๋Ÿฌ ์„ค์น˜
npm install -g typescript

# ๋ฒ„์ „ ํ™•์ธ
tsc --version

# ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™” (tsconfig.json ์ƒ์„ฑ)
tsc --init

# ์ปดํŒŒ์ผ
tsc index.ts

# ๊ฐ์‹œ ๋ชจ๋“œ (ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ์ปดํŒŒ์ผ)
tsc --watch

tsconfig.json ์ฃผ์š” ์˜ต์…˜

{
  "compilerOptions": {
    "target": "ES2020",          // ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ๋ฌผ์˜ JavaScript ๋ฒ„์ „
    "module": "commonjs",        // ๋ชจ๋“ˆ ์‹œ์Šคํ…œ (commonjs / ESNext)
    "lib": ["ES2020", "DOM"],    // ์‚ฌ์šฉํ•  ๋‚ด์žฅ ํƒ€์ž… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    "outDir": "./dist",          // ์ปดํŒŒ์ผ ๊ฒฐ๊ณผ๋ฌผ ์ €์žฅ ์œ„์น˜
    "rootDir": "./src",          // TypeScript ์†Œ์Šค ํŒŒ์ผ ์œ„์น˜
    "strict": true,              // ์—„๊ฒฉ ๋ชจ๋“œ (๊ฐ•๋ ฅ ๊ถŒ์žฅ!)
    "esModuleInterop": true,     // CommonJS ๋ชจ๋“ˆ import ํŽธ์˜์„ฑ
    "skipLibCheck": true,        // node_modules ํƒ€์ž… ๊ฒ€์‚ฌ ์ƒ๋žต
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

"strict": true๋Š” ๋ฐ˜๋“œ์‹œ ์ผœ๋‘์ž. strictNullChecks, noImplicitAny ๋“ฑ ํ•ต์‹ฌ ์˜ต์…˜๋“ค์ด ๋ชจ๋‘ ํ™œ์„ฑํ™”๋œ๋‹ค.

Vite + React + TypeScript ์‹œ์ž‘

# Vite๋กœ React + TypeScript ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev

๊ธฐ๋ณธ ํƒ€์ž… ์™„์ „ ์ •๋ฆฌ

์›์‹œ ํƒ€์ž…

// ๊ธฐ๋ณธ ์›์‹œ ํƒ€์ž…
const name: string = 'Alice';
const age: number = 25;
const isActive: boolean = true;

// null๊ณผ undefined
let value: null = null;
let data: undefined = undefined;

// symbol
const id: symbol = Symbol('id');

// bigint
const bigNum: bigint = 9007199254740991n;

๋ฐฐ์—ด๊ณผ ํŠœํ”Œ

// ๋ฐฐ์—ด โ€” ๋‘ ๊ฐ€์ง€ ํ‘œ๊ธฐ๋ฒ•
const numbers: number[] = [1, 2, 3];
const strings: Array<string> = ['a', 'b', 'c'];

// ์ฝ๊ธฐ ์ „์šฉ ๋ฐฐ์—ด
const readOnly: readonly number[] = [1, 2, 3];
// readOnly.push(4); // ์˜ค๋ฅ˜!

// ํŠœํ”Œ โ€” ๊ฐ ์œ„์น˜์˜ ํƒ€์ž…์ด ๊ณ ์ •๋จ
const point: [number, number] = [10, 20];
const entry: [string, number] = ['Alice', 25];

// named tuple (TypeScript 4.0+)
const namedPoint: [x: number, y: number] = [10, 20];

any, unknown, never, void ๋น„๊ต

ํƒ€์ž… ์˜๋ฏธ ํƒ€์ž… ์ฒดํฌ ์‚ฌ์šฉ ์‹œ์ 
any ๋ชจ๋“  ํƒ€์ž… ํ—ˆ์šฉ, ํƒ€์ž… ์ฒดํฌ ์—†์Œ โŒ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ดˆ๊ธฐ, ์–ด์ฉ” ์ˆ˜ ์—†์„ ๋•Œ๋งŒ
unknown ๋ชจ๋“  ํƒ€์ž… ํ—ˆ์šฉ, ์‚ฌ์šฉ ์ „ ํƒ€์ž… ์ฒดํฌ ํ•„์š” โœ… API ์‘๋‹ต ๋“ฑ ํƒ€์ž… ๋ถˆ๋ช…ํ™•ํ•  ๋•Œ
never ์ ˆ๋Œ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฐ’ โœ… ํ•จ์ˆ˜๊ฐ€ ํ•ญ์ƒ throw, ๋ฌดํ•œ ๋ฃจํ”„
void ๋ฐ˜ํ™˜๊ฐ’ ์—†์Œ โœ… ๋ฐ˜ํ™˜๊ฐ’์ด ์—†๋Š” ํ•จ์ˆ˜
// any โ€” ํ”ผํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค
let anything: any = 42;
anything = 'string';      // OK
anything.foo.bar.baz;     // OK (๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ์œ„ํ—˜!)

// unknown โ€” any๋ณด๋‹ค ์•ˆ์ „
let value: unknown = 42;
value.toFixed(2);         // ์˜ค๋ฅ˜: ํƒ€์ž… ์ฒดํฌ ์—†์ด ์‚ฌ์šฉ ๋ถˆ๊ฐ€
if (typeof value === 'number') {
  value.toFixed(2);       // OK: ํƒ€์ž… ๊ฐ€๋“œ ํ›„ ์‚ฌ์šฉ
}

// never โ€” ๋„๋‹ฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ
function throwError(msg: string): never {
  throw new Error(msg);
  // ์—ฌ๊ธฐ ์ดํ›„ ์ฝ”๋“œ๋Š” never ํƒ€์ž…
}

// void โ€” ๋ฐ˜ํ™˜๊ฐ’ ์—†๋Š” ํ•จ์ˆ˜
function logMessage(msg: string): void {
  console.log(msg);
  // return 42; // ์˜ค๋ฅ˜
}

ํƒ€์ž… ์ถ”๋ก  (Type Inference)

๋ชจ๋“  ๋ณ€์ˆ˜์— ํƒ€์ž…์„ ๋ช…์‹œํ•  ํ•„์š”๋Š” ์—†๋‹ค. TypeScript๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ํƒ€์ž…์„ ์•Œ์•„์„œ ์ถ”๋ก ํ•œ๋‹ค:

// TypeScript๊ฐ€ ํƒ€์ž…์„ ์ž๋™์œผ๋กœ ์ถ”๋ก 
const name = 'Alice';   // string์œผ๋กœ ์ถ”๋ก 
const age = 25;         // number๋กœ ์ถ”๋ก 
const arr = [1, 2, 3];  // number[]๋กœ ์ถ”๋ก 

// ํ•จ์ˆ˜ ๋ฐ˜ํ™˜ ํƒ€์ž…๋„ ์ถ”๋ก 
function add(a: number, b: number) {
  return a + b; // ๋ฐ˜ํ™˜ ํƒ€์ž…: number (์ถ”๋ก ๋จ)
}

// ๋ณต์žกํ•œ ๊ฒฝ์šฐ์—” ๋ช…์‹œํ•˜๋Š” ๊ฒŒ ์ข‹์Œ
function getUser(id: number): User {
  // ๋ช…์‹œ์ ์œผ๋กœ ์“ฐ๋ฉด ์˜๋„๊ฐ€ ๋ช…ํ™•ํ•˜๊ณ  ์˜ค๋ฅ˜๋„ ๋นจ๋ฆฌ ๋ฐœ๊ฒฌ
}

์ธํ„ฐํŽ˜์ด์Šค vs ํƒ€์ž… ๋ณ„์นญ

TypeScript์—์„œ ๊ฐ์ฒด ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๋‹ค.

interface

interface User {
  id: number;
  name: string;
  email?: string;      // optional (์žˆ์–ด๋„ ๋˜๊ณ  ์—†์–ด๋„ ๋จ)
  readonly createdAt: Date;  // ํ•œ ๋ฒˆ ์„ค์ •ํ•˜๋ฉด ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€
}

// ์ธํ„ฐํŽ˜์ด์Šค ํ™•์žฅ
interface Admin extends User {
  role: 'admin' | 'superadmin';
  permissions: string[];
}

// ์„ ์–ธ ๋ณ‘ํ•ฉ (๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฒˆ ์„ ์–ธํ•˜๋ฉด ํ•ฉ์ณ์ง)
interface Window {
  myCustomProp: string; // ๋ธŒ๋ผ์šฐ์ € ์ „์—ญ ๊ฐ์ฒด ํ™•์žฅ ๊ฐ€๋Šฅ
}

type alias

type User = {
  id: number;
  name: string;
  email?: string;
};

// Union ํƒ€์ž…
type ID = string | number;
type Status = 'active' | 'inactive' | 'pending';

// Intersection ํƒ€์ž… (๋ชจ๋“  ํƒ€์ž…์˜ ์†์„ฑ์„ ํ•ฉ์นจ)
type AdminUser = User & { role: string; permissions: string[] };

// ํ•จ์ˆ˜ ํƒ€์ž…
type Handler = (event: MouseEvent) => void;

์–ธ์ œ interface vs type?

interface type
๊ฐ์ฒด ํƒ€์ž… ์ •์˜ โœ… โœ…
Union / Intersection โŒ โœ…
์„ ์–ธ ๋ณ‘ํ•ฉ โœ… โŒ
extends โœ… &๋กœ ๊ฐ€๋Šฅ
์›์‹œ ํƒ€์ž… ๋ณ„์นญ โŒ โœ…

์‹ค๋ฌด ๊ฐ€์ด๋“œ๋ผ์ธ:

  • interface: ๊ฐ์ฒด(ํด๋ž˜์Šค, API ์‘๋‹ต ๋“ฑ) ํƒ€์ž… ์ •์˜ โ€” ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์„ ๋•Œ
  • type: Union, Intersection, ํ•จ์ˆ˜ ํƒ€์ž…, ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ์กฐํ•ฉ โ€” ์œ ์—ฐํ•œ ํƒ€์ž…์ด ํ•„์š”ํ•  ๋•Œ
  • ํŒ€์ด ์žˆ๋‹ค๋ฉด ์ผ๊ด€์„ฑ์ด ๊ฐ€์žฅ ์ค‘์š”. ํ•˜๋‚˜๋กœ ํ†ต์ผํ•ด์„œ ์“ฐ๋Š” ๊ฒŒ ๋‚ซ๋‹ค.

ํ•จ์ˆ˜ ํƒ€์ž…

// ๊ธฐ๋ณธ ํ•จ์ˆ˜ ํƒ€์ž…
function add(a: number, b: number): number {
  return a + b;
}

// ํ™”์‚ดํ‘œ ํ•จ์ˆ˜
const multiply = (a: number, b: number): number => a * b;

// ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜ (?)
function greet(name: string, greeting?: string): string {
  return `${greeting ?? 'Hello'}, ${name}!`;
}

// ๊ธฐ๋ณธ๊ฐ’
function greet(name: string, greeting: string = 'Hello'): string {
  return `${greeting}, ${name}!`;
}

// ๋‚˜๋จธ์ง€ ๋งค๊ฐœ๋ณ€์ˆ˜
function sum(...numbers: number[]): number {
  return numbers.reduce((acc, n) => acc + n, 0);
}

// ํ•จ์ˆ˜ ์˜ค๋ฒ„๋กœ๋”ฉ
function format(value: string): string;
function format(value: number): string;
function format(value: string | number): string {
  if (typeof value === 'string') return value.toUpperCase();
  return value.toFixed(2);
}

์ œ๋„ค๋ฆญ (Generics)

์ œ๋„ค๋ฆญ์€ ํƒ€์ž…์„ ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ๋‹ค๋ฃจ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. "์–ด๋–ค ํƒ€์ž…์ด๋“  ๋™์ž‘ํ•˜๋˜, ํƒ€์ž… ์•ˆ์ „์„ฑ์€ ์œ ์ง€ํ•˜๊ฒ ๋‹ค"๋Š” ๋œป์ด๋‹ค.

// ์ œ๋„ค๋ฆญ ์—†์ด โ€” ํƒ€์ž… ์ •๋ณด ์†์‹ค
function identity(value: any): any {
  return value; // ๋ฐ˜ํ™˜ ํƒ€์ž…์ด any๋ผ ํƒ€์ž… ์ •๋ณด๊ฐ€ ์—†์Œ
}

// ์ œ๋„ค๋ฆญ ์‚ฌ์šฉ โ€” ํƒ€์ž… ์ •๋ณด ์œ ์ง€
function identity<T>(value: T): T {
  return value;
}

const num = identity(42);      // T = number, ๋ฐ˜ํ™˜ ํƒ€์ž… number
const str = identity('hello'); // T = string, ๋ฐ˜ํ™˜ ํƒ€์ž… string

์ œ๋„ค๋ฆญ ์ œ์•ฝ (Constraints)

// T๋Š” ๋ฐ˜๋“œ์‹œ length ์†์„ฑ์„ ๊ฐ€์ ธ์•ผ ํ•จ
function getLength<T extends { length: number }>(value: T): number {
  return value.length;
}

getLength('hello');      // OK (string์€ length๊ฐ€ ์žˆ์Œ)
getLength([1, 2, 3]);    // OK (๋ฐฐ์—ด์€ length๊ฐ€ ์žˆ์Œ)
getLength(42);           // ์˜ค๋ฅ˜! (number๋Š” length ์—†์Œ)

์‹ค์šฉ์ ์ธ ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜ ์˜ˆ์‹œ

// API ์‘๋‹ต์„ ์ œ๋„ค๋ฆญ์œผ๋กœ ๊ฐ์‹ธ๊ธฐ
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
  const response = await fetch(url);
  return response.json();
}

// ์‚ฌ์šฉ
interface User {
  id: number;
  name: string;
}

const result = await fetchData<User>('/api/users/1');
result.data.name; // TypeScript๊ฐ€ User ํƒ€์ž…์ž„์„ ์•Œ๊ณ  ์ž๋™์™„์„ฑ ์ œ๊ณต

// ์ฒซ ๋ฒˆ์งธ ์š”์†Œ ๋ฐ˜ํ™˜
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

// ํ‚ค๋กœ ๊ฐ์ฒด ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { id: 1, name: 'Alice' };
getProperty(user, 'name');  // string ๋ฐ˜ํ™˜
getProperty(user, 'email'); // ์˜ค๋ฅ˜! 'email'์€ user์˜ ํ‚ค๊ฐ€ ์•„๋‹˜

์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… (Utility Types)

TypeScript๋Š” ์ž์ฃผ ์“ฐ๋Š” ํƒ€์ž… ๋ณ€ํ™˜ ํŒจํ„ด์„ ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์œผ๋กœ ์ œ๊ณตํ•œ๋‹ค.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

// Partial<T> โ€” ๋ชจ๋“  ์†์„ฑ์„ optional๋กœ ๋งŒ๋“ฆ
type UserUpdate = Partial<User>;
// { id?: number; name?: string; email?: string; ... }

// Required<T> โ€” ๋ชจ๋“  ์†์„ฑ์„ ํ•„์ˆ˜๋กœ ๋งŒ๋“ฆ
type RequiredUser = Required<Partial<User>>;

// Readonly<T> โ€” ๋ชจ๋“  ์†์„ฑ์„ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ๋งŒ๋“ฆ
type FrozenUser = Readonly<User>;
// frozenUser.name = 'Bob'; // ์˜ค๋ฅ˜!

// Pick<T, K> โ€” ํŠน์ • ์†์„ฑ๋งŒ ์„ ํƒ
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string }

// Omit<T, K> โ€” ํŠน์ • ์†์„ฑ ์ œ์™ธ
type PublicUser = Omit<User, 'password'>;
// { id, name, email, createdAt } (password ์ œ์™ธ)

// Record<K, V> โ€” ํ‚ค-๊ฐ’ ๋งต ํƒ€์ž…
type RolePermissions = Record<string, string[]>;
const permissions: RolePermissions = {
  admin: ['read', 'write', 'delete'],
  user: ['read'],
};

// Exclude<T, U> โ€” Union์—์„œ ํŠน์ • ํƒ€์ž… ์ œ์™ธ
type Status = 'active' | 'inactive' | 'pending' | 'deleted';
type ActiveStatus = Exclude<Status, 'deleted' | 'inactive'>;
// 'active' | 'pending'

// Extract<T, U> โ€” Union์—์„œ ํŠน์ • ํƒ€์ž…๋งŒ ์ถ”์ถœ
type Numeric = Extract<string | number | boolean, number>;
// number

// ReturnType<T> โ€” ํ•จ์ˆ˜ ๋ฐ˜ํ™˜ ํƒ€์ž… ์ถ”์ถœ
function getUser() {
  return { id: 1, name: 'Alice' };
}
type UserType = ReturnType<typeof getUser>;
// { id: number; name: string }

// Parameters<T> โ€” ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž… ์ถ”์ถœ
function createUser(name: string, age: number, email: string) {}
type CreateUserParams = Parameters<typeof createUser>;
// [string, number, string]

์‹ค์ „ ํ™œ์šฉ ์˜ˆ์‹œ

interface Todo {
  id: number;
  title: string;
  completed: boolean;
  dueDate: Date;
}

// API์—์„œ ๋ฐ›๋Š” ์ƒ์„ฑ ์š”์ฒญ: id์™€ ์™„๋ฃŒ ์—ฌ๋ถ€๋Š” ์„œ๋ฒ„๊ฐ€ ์ •ํ•จ
type CreateTodoRequest = Omit<Todo, 'id' | 'completed'>;
// { title: string; dueDate: Date }

// ์—…๋ฐ์ดํŠธ ์š”์ฒญ: id๋Š” ํ•„์ˆ˜, ๋‚˜๋จธ์ง€๋Š” ์„ ํƒ
type UpdateTodoRequest = Pick<Todo, 'id'> & Partial<Omit<Todo, 'id'>>;
// { id: number; title?: string; completed?: boolean; dueDate?: Date }

React + TypeScript ์‹ค์ „

Props ํƒ€์ž… ์ •์˜

// interface๋กœ Props ์ •์˜
interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: 'primary' | 'secondary' | 'danger';
  disabled?: boolean;
  children?: React.ReactNode;
}

// ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ
const Button: React.FC<ButtonProps> = ({
  label,
  onClick,
  variant = 'primary',
  disabled = false,
}) => (
  <button
    onClick={onClick}
    disabled={disabled}
    className={`btn btn-${variant}`}
  >
    {label}
  </button>
);

// React.FC ์—†์ด (๋” ์œ ์—ฐํ•œ ๋ฐฉ์‹)
function Card({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div>
      <h2>{title}</h2>
      {children}
    </div>
  );
}

useState์™€ ํƒ€์ž…

// ํƒ€์ž… ์ถ”๋ก ์œผ๋กœ ์ถฉ๋ถ„ํ•œ ๊ฒฝ์šฐ
const [count, setCount] = useState(0);         // number
const [name, setName] = useState('');          // string
const [isOpen, setIsOpen] = useState(false);   // boolean

// ํƒ€์ž…์„ ๋ช…์‹œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<string[]>([]);

// ๋ณต์žกํ•œ ์ƒํƒœ
interface FormState {
  username: string;
  email: string;
  errors: Record<string, string>;
}

const [form, setForm] = useState<FormState>({
  username: '',
  email: '',
  errors: {},
});

useRef์™€ ํƒ€์ž…

// DOM ์š”์†Œ ์ฐธ์กฐ
const inputRef = useRef<HTMLInputElement>(null);

// ๊ฐ’ ์œ ์ง€ (DOM ์•„๋‹Œ ๊ฒฝ์šฐ)
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

// ์‚ฌ์šฉ
const focusInput = () => {
  inputRef.current?.focus(); // optional chaining ํ•„์š”
};

์ด๋ฒคํŠธ ํƒ€์ž…

// ์ž์ฃผ ์“ฐ๋Š” ์ด๋ฒคํŠธ ํƒ€์ž…๋“ค
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  console.log(e.currentTarget.name);
};

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setName(e.target.value);
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
};

const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
  if (e.key === 'Enter') doSearch();
};

์ž์ฃผ ํ•˜๋Š” ์‹ค์ˆ˜์™€ ํ•ด๊ฒฐ๋ฒ•

โ‘  any ๋‚จ์šฉ

// โŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์“ฐ๋Š” ์˜๋ฏธ๊ฐ€ ์—†์–ด์ง
function processData(data: any) {
  return data.result.items.map((item: any) => item.value);
}

// โœ… ์‹ค์ œ ํƒ€์ž…์„ ์ •์˜ํ•˜๊ฑฐ๋‚˜ unknown + ํƒ€์ž… ๊ฐ€๋“œ ์‚ฌ์šฉ
interface ApiData {
  result: {
    items: Array<{ value: string }>;
  };
}

function processData(data: unknown) {
  if (isApiData(data)) {
    return data.result.items.map(item => item.value);
  }
  throw new Error('Invalid data format');
}

โ‘ก Type Assertion(as) ๋‚จ์šฉ

// โŒ ์œ„ํ—˜ โ€” ์‹ค์ œ๋กœ๋Š” User๊ฐ€ ์•„๋‹ ์ˆ˜ ์žˆ์Œ
const user = JSON.parse(jsonString) as User;
user.name.toUpperCase(); // ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ๊ฐ€๋Šฅ

// โœ… ๊ฒ€์ฆ ํ›„ ์‚ฌ์šฉ
function parseUser(json: string): User {
  const data = JSON.parse(json);
  if (typeof data.name !== 'string') throw new Error('Invalid user');
  return data as User; // ๊ฒ€์ฆ ํ›„ assertion์€ ์•ˆ์ „
}

โ‘ข Non-null assertion(!) ๋‚จ์šฉ

// โŒ !๋ฅผ ์Šต๊ด€์ ์œผ๋กœ ๋ถ™์ด๋ฉด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ์˜๋ฏธ ์—†์Œ
const element = document.getElementById('root')!;

// โœ… ์‹ค์ œ๋กœ null์ด ์•„๋‹˜์„ ํ™•์ธ ํ›„ ์‚ฌ์šฉ
const element = document.getElementById('root');
if (!element) throw new Error('root element not found');
// ์ด์ œ element๋Š” HTMLElement

JavaScript โ†’ TypeScript ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „๋žต

์ด๋ฏธ JavaScript๋กœ ์ž‘์„ฑ๋œ ํ”„๋กœ์ ํŠธ๋ฅผ TypeScript๋กœ ์ „ํ™˜ํ•  ๋•Œ:

1๋‹จ๊ณ„: tsconfig.json ์ถ”๊ฐ€, .js โ†’ .ts ํ™•์žฅ์ž ๋ณ€๊ฒฝ
       "allowJs": true, "strict": false ๋กœ ์‹œ์ž‘

2๋‹จ๊ณ„: ํ•ต์‹ฌ ๋ชจ๋“ˆ๋ถ€ํ„ฐ ํƒ€์ž… ์ถ”๊ฐ€
       any๋ฅผ ์ผ๋‹จ ํ—ˆ์šฉํ•˜๋ฉด์„œ ์กฐ๊ธˆ์”ฉ ํƒ€์ž… ์ •์˜

3๋‹จ๊ณ„: strict ์˜ต์…˜ ํ™œ์„ฑํ™”
       any๋ฅผ ์‹ค์ œ ํƒ€์ž…์œผ๋กœ ๋Œ€์ฒด
       strictNullChecks ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ

4๋‹จ๊ณ„: any ์ œ๊ฑฐ, ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ํ™œ์šฉ
       ์™„์ „ํ•œ TypeScript ์ฝ”๋“œ๋ฒ ์ด์Šค

ํ•œ ๋ฒˆ์— ์ „ํ™˜ํ•˜๋ ค ํ•˜์ง€ ๋ง๊ณ , ํŒŒ์ผ ๋‹จ์œ„๋กœ ์ ์ง„์ ์œผ๋กœ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์‹ค์šฉ์ ์ด๋‹ค.


TypeScript๋Š” ์ฒ˜์Œ์—” ํƒ€์ž… ์˜ค๋ฅ˜์™€ ์”จ๋ฆ„ํ•˜๋Š” ๊ฒƒ ๊ฐ™์•„์„œ ๋ฒˆ๊ฑฐ๋กญ๊ฒŒ ๋А๊ปด์งˆ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก TypeScript๊ฐ€ ์ œ๊ณตํ•˜๋Š” IDE ์ž๋™์™„์„ฑ, ๋ฆฌํŒฉํ† ๋ง ์•ˆ์ „์„ฑ, ๋ฒ„๊ทธ ์‚ฌ์ „ ์ฐจ๋‹จ์˜ ๊ฐ€์น˜๊ฐ€ ํ›จ์”ฌ ์ปค์ง„๋‹ค.

๋‹ค์Œ ๊ธ€์—์„œ๋Š” TypeScript๋กœ React ์•ฑ์„ ์‹ค์ „์—์„œ ์–ด๋–ป๊ฒŒ ๊ตฌ์กฐํ™”ํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  API ๋ ˆ์ด์–ด๋ฅผ ์–ด๋–ป๊ฒŒ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š”์ง€ ๋‹ค๋ฃฐ ์˜ˆ์ •์ด๋‹ค.


๊ด€๋ จ ๊ธ€

@leekh8
๋ณด์•ˆ, ์›น ๊ฐœ๋ฐœ, Python์„ ๋‹ค๋ฃจ๋Š” ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ