TypeScript를 싫어하는 JavaScript 개발자를 위한 15분 안에 배우는 TypeScript
Source: Dev.to

그것은 의도와 규율, 그리고 증거가 있는 JavaScript입니다.
핵심적으로 TypeScript는 단순히 JavaScript에 types와 generics를 더한 것일 뿐—그 이상도, 마법 같은 것도 없습니다. 새로운 런타임 동작을 추가하지 않으며, 코드를 작성할 때 자신(또는 팀원)에게 거짓말을 하지 않도록 보장해 줍니다.
이미 JavaScript를 알고 있다면, TypeScript에서 실제로 중요한 것이 무엇인지 가장 빠르게 이해할 수 있는 방법입니다.
JavaScript vs. TypeScript: 타입에 관한 진실
TypeScript는 새로운 원시 데이터 타입을 도입하지 않습니다.
이미 알고 있는 모든 것이 여전히 존재합니다:
stringnumberbooleannullundefinedsymbolbigint
TypeScript의 역할은 발명이 아니라 강제입니다.
- JavaScript: “런타임에 알아낼 겁니다.”
- TypeScript: “실행하기 전에 증명하세요.”
Methods Are the Same — Safety Is Not
All your familiar methods still exist:
- Strings →
concat,slice,charAt - Arrays →
map,filter,reduce,find
The difference is that TypeScript knows what you’re allowed to call.
let nums: number[] = [1, 2, 3];
nums.map(n => n * 2); // ✅ OK
nums.map(n => n.toUpperCase()); // ❌ Error – caught at compile time
JavaScript would let the second line fail at runtime; TypeScript stops it at compile time. That’s the entire value proposition.
타입 주석 vs. 타입 추론
명시적으로 주석을 달 수 있습니다:
let name: string = "Ram";
하지만 TypeScript는 더 똑똑합니다:
let name = "Ram"; // `string`으로 추론됨
TypeScript는 타입을 자동으로 추적합니다. 이것을 타입 추론이라고 하며, 변수 선언을 한 곳에서 하고 다른 곳에서 값을 변경하는 것이 보통 좋지 않은 이유입니다.
any 트랩 (이렇게 하지 마세요)
많은 사람들이 TypeScript를 “벗어나기” 위해 any를 사용합니다. 이는 TypeScript의 목적을 무색하게 합니다.
let value: any = 10;
value.toUpperCase(); // ✅ TS는 허용하지만 런타임에서는 오류 발생
그래서 tsconfig.json에 noImplicitAny 옵션이 존재합니다. 코드 전역에 any를 남발한다면, 사실상 추가적인 단계만 더한 순수 JavaScript를 작성하는 것과 같습니다.
Source:
함수: 매개변수 및 반환도 중요합니다
TypeScript는 입력만 중요한 것이 아니라, 출력도 중요합니다.
function addTwo(num: number): number {
return num + 2;
}
반환 타입을 명시하지 않으면 다음 코드도 컴파일되지만, 이는 버그가 됩니다:
function addTwo(num: number) {
return "hello"; // ❌ 반환 타입을 선언하면 타입 오류 발생
}
TypeScript는 계약의 양쪽을 모두 고정시킬 수 있게 해줍니다.
특수 반환 타입
void→ 함수가 부수 효과만 수행함never→ 함수가 절대 반환하지 않음 (예외를 발생시키거나 크래시)
function handleError(msg: string): void {
console.log(msg);
}
function crash(msg: string): never {
throw new Error(msg);
}
팀에서 반환 타입이 중요한 이유
잘 타입이 지정된 함수는 본문을 읽지 않아도 이야기를 전달한다:
function getCourse(): { title: string; price: number } {
return { title: "TypeScript Mastery", price: 499 };
}
누구든지 즉시 확인할 수 있습니다:
- 무엇을 반환하는지
- 정확한 구조
- 필수 항목이 무엇인지
이는 단순한 문법을 넘어 팀 커뮤니케이션입니다.
Type Aliases: Naming Shapes
Type aliases를 사용하면 의도에 이름을 부여할 수 있습니다.
type User = {
name: string;
email: string;
isActive: boolean;
};
JavaScript는 “trust me.”라고 말합니다.
TypeScript는 “prove it.”라고 말합니다.
readonly, 선택적, 그리고 유니온 타입
readonly
readonly _id: string;
읽을 수는 있지만, 변경할 수는 없습니다.
선택적 속성
creditCard?: number;
속성이 존재할 수도 있고 없을 수도 있습니다; TypeScript는 이를 확인하도록 강제합니다.
유니온 타입
let id: number | string;
다음과 같은 경우에 많이 사용됩니다:
- 역할 기반 접근 제어 (RBAC)
- API 응답
- 조건부 흐름
튜플: 제어된 혼돈
튜플은 순서가 정해진, 타입이 지정된 배열입니다:
let user: [string, number, boolean];
- 이것은 TypeScript에서만 존재하며, 컴파일된 JavaScript는 신경 쓰지 않습니다.
push()도 여전히 동작하지만 (가능하면 적게 사용하세요).- 순서가 정말 중요한 경우에만 사용하세요.
열거형: 명명된 상수
const enum SeatChoice {
aisle = 10,
middle,
window
}
- 가독성이 좋고 예측 가능함.
- 열거형이 컴파일 시 인라인되므로 런타임 모호성이 없음.
Source: …
인터페이스 vs. 타입 (짧은 버전)
인터페이스
- 확장 가능.
- 클래스와 아름답게 작동하며 OOP 계약을 강제합니다.
interface IUser {
email: string;
startTrial(): string;
}
- 인터페이스는 재오픈(선언 병합)할 수 있습니다.
- 타입은 재오픈할 수 없습니다.
타입
- 유니언, 인터섹션 및 원시 별칭에 유용합니다.
- 병합할 수 없지만 일회성 형태에 적합합니다.
Classes, Constructors, and Access Modifiers
TypeScript는 OOP를 덜 고통스럽게 만들어 줍니다:
class User {
constructor(
public email: string,
public name: string,
private userId: string
) {}
}
Access Modifiers
| Modifier | Visibility |
|---|---|
public | Anywhere |
private | Inside the class only |
protected | Inside the class and its subclasses |
이러한 수정자는 실제 시스템에서 “접근 혼란”을 방지하는 데 도움이 됩니다.
인터페이스 + 클래스 = 구조적 무결성
인터페이스 (형태)
interface TakePhoto {
cameraMode: string;
filter: string;
}
클래스 (동작)
class Camera implements TakePhoto {
cameraMode = "auto";
filter = "none";
snap() { /* … */ }
}
인터페이스는 존재해야 할 것을 정의하고; 클래스는 구현을 제공합니다.
이 패턴은 백엔드(및 프론트엔드) 시스템에서 매우 잘 확장됩니다.
추상 클래스: 인스턴스화 없이 의도
abstract class TakePhoto {
abstract getSepia(): void;
}
- 직접 인스턴스화할 수 없습니다.
- 서브클래스는 반드시 추상 멤버를 구현해야 하며, 이를 통해 필요한 동작을 보장합니다.
Generics: The Real Power Move
Generics let you write logic once – safely:
function identity<T>(val: T): T {
return val;
}
Common places where generics shine:
- API client wrappers
- Repository patterns
- Response models
They’re not an OOP replacement; they’re an evolution of type safety.
타입 좁히기: 런타임 안전성
TypeScript는 절대 “추측”하지 않습니다 – 타입을 명시적으로 좁히는 방법은 다음과 같습니다:
typeof체크instanceof체크in연산자
이러한 기술은 컴파일러를 만족시키면서 리플렉션 없이 올바른 런타임 동작을 보장합니다.
Discriminated Unions (Cleanest Pattern)
interface CreditCard {
paymentType: "card";
cardNumber: string;
}
interface PayPal {
paymentType: "paypal";
email: string;
}
type PaymentMethod = CreditCard | PayPal;
- 단일 구분 필드(
paymentType)는 모호함이 전혀 없습니다. switch문이나 전체를 포괄하는if검사에 완벽합니다.
Conclusion
- TypeScript는 당신을 느리게 하지 않는다 – 불명확한 코드가 그렇다.
- 모호함을 불법으로 만들어 명시적이고 유지보수 가능하며 타입‑안전한 코드를 작성하도록 강제한다.