2025-07-20 13:32
Status:
타입스크립트 치트 시트
타입(Type)
추가 설명
- 타입(alias)은 코드의 가독성과 재사용성을 높여주며, 복잡한 구조를 간단하게 표현할 수 있습니다.
- 타입은 유니온, 인터섹션, 매핑, 조건부 등 다양한 조합이 가능하여, 복잡한 데이터 구조를 유연하게 설계할 수 있습니다.
- 타입은 런타임에 영향을 주지 않으며, 오직 컴파일 타임에만 사용됩니다.
- 타입스크립트의 타입 시스템은 점진적으로 적용할 수 있어, 기존 JavaScript 코드에 점진적으로 타입을 추가할 수 있습니다.
- 타입 별칭은 복잡한 타입을 한 번에 정의하고, 여러 곳에서 재사용할 때 매우 유용합니다.
- 타입스크립트의 타입은 실제 코드 실행에는 영향을 주지 않으므로, 타입 관련 코드는 빌드 시 제거됩니다.
주요 포인트 (Key Points)
- 전체 이름은 **“type alias”**이며, 타입 리터럴에 이름을 제공하는 데 사용됩니다.
- 인터페이스보다 더 풍부한 타입 시스템 기능을 지원합니다.
타입 (Type) vs 인터페이스 (Interface)
- 인터페이스는 객체 모양만 설명할 수 있습니다.
- 인터페이스는 확장될 수 있으며, 여러 번 선언 가능
- 인터페이스 비교 검사는 더 빠를 수 있습니다.
타입을 변수처럼 생각하세요.(Thick of Types Like Variables)
- 다른 스코프에서 동일한 이름의 변수를 생성할 수 있는 것처럼, 타입도 유사한 의미를 가집니다. (Much like how you can create variables with the same name in different scopes, a type has similar semantics.)
유틸리티 타입으로 빌드하세요.
- TypeScript에는 타입 시스템에서 일반적인 작업을 돕는 많은 전역 타입이 포함되어 있습니다. 사이트에서 확인하세요.
객체 리터럴 구문 (Object Literal Syntax)
type JSONResponse = {
version: number; // 필드
/** In bytes */ // 첨부 문서
payloadSize: number; //
outOfStock?: boolean; // 선택적 //
update: (retryTimes: number) => void; // 화살표 함수 필드 //
update(retryTimes: number): void; // 함수 //
(): JSONResponse; // 타입은 호출 가능 //
[key: string]: number; // 임의 인덱스 허용 //
new (s: string): JSONResponse; // new 가능 //
readonly body: string; // 읽기 전용 속성 //
}
// 공간 절약을 위해 간결하게 작성(Terser for saving space), 자세한 내용은 Interface Cheat Sheet 참고, 'static'을 제외한 모든 것이 일치합니다.
원시 타입 (Primitive Type)
type SanitizedInput = string;
type MissingNo = 404;
객체 리터럴 타입 (Object Literal Type)
type Location = { x: number; y: number };
튜플 타입 (Tuple Type)
- 튜플은 특정 인덱스에서 알려진 타입을 가진 특별한 배열입니다.
type Data = [
location: Location,
timestamp: string
];
유니온 타입 (Union Type)
- 여러 옵션 중 하나인 타입을 설명합니다. 예를 들어, 알려진 문자열 목록입니다.
type Size = "small" | "medium" | "large";
교차 타입 (Intersection Types)
- 타입을 병합/확장하는 방법입니다.
type Location = { x: number } & { y: number };
// { x: number, y: number }
타입 인덱싱 (Type Indexing)
- 타입에서 하위 집합을 추출하고 이름을 지정하는 방법입니다.
type Response = { data: { ... } };
type Data = Response["data"];
// { ... }
타입을 값에서 추출하기 (Type from Value)
- 기존 JavaScript 런타임 값에서 typeof 연산자를 사용하여 타입을 재사용합니다.
const data = { ... };
type Data = typeof data;
함수 반환값에서 타입 추출 (Type from Func Return)
- 함수의 반환 값을 타입으로 재사용합니다.
function createFixtures() { ... }
type Fixtures = ReturnType<typeof createFixtures>;
function test(fixture: Fixtures) {}
모듈에서 타입 추출 (Type from Module)
- 모듈에서 타입을 재사용합니다.
const data: import("./data").data;
매핑된 타입 (Mapped Types)
- 타입 시스템의 map 문과 유사하게 작동하여 입력 타입이 새 타입의 구조를 변경할 수 있습니다.
type Artist = { name: string, bio: string };
type Subscriber<Type> = {
// 타입 제네릭 파라미터 "Type"의 각 필드를 순회합니다. (Loop through each field in the type generic parameter “Type”)
// 원래 타입을 파라미터로 받는 함수 타입으로 설정합니다. (Sets type as a function with original type as param)
[Property in keyof Type]: (newValue: Type[Property]) => void;
};
type ArtistSub = Subscriber<Artist>;
// { name: (nv: string) => void,
// bio: (nv: string) => void }
조건부 타입 (Conditional Types)
- 타입 시스템 내에서 “if 문”처럼 작동합니다. 제네릭을 통해 생성되며, 일반적으로 타입 유니온의 옵션 수를 줄이는 데 사용됩니다.
type HasFourLegs<Animal> = Animal extends { legs: 4 } ? Animal : never;
type Animals = Bird | Dog | Ant | Wolf;
type FourLegs = HasFourLegs<Animals>;
// Dog | Wolf
템플릿 유니온 타입 (Template Union Types)
- 템플릿 문자열을 사용하여 타입 시스템 내에서 텍스트를 결합하고 조작할 수 있습니다.
type SupportedLangs = "en" | "pt" | "zh";
type FooterLocaleIDs = "header" | "footer";
type AllLocaleIDs = `${SupportedLangs}_${FooterLocaleIDs}_id`;
// "en_header_id" | "en_footer_id"
// "pt_header_id" | "pt_footer_id"
// "zh_header_id" | "zh_footer_id"
클래스(Class)
추가 설명
- 클래스는 객체지향 프로그래밍(OOP)의 핵심 개념으로, 데이터(필드)와 동작(메서드)을 하나의 단위로 묶어줍니다.
- 타입스크립트 클래스는 JavaScript의 클래스 문법을 확장하여, 타입 안전성과 코드 자동완성 등 개발 생산성을 높여줍니다.
- 클래스의 private, protected, public 접근 제어자는 코드의 캡슐화(encapsulation)를 강화합니다.
- 추상 클래스(abstract class)는 공통 동작을 정의하고, 하위 클래스에서 구체적으로 구현하도록 강제합니다.
- 데코레이터(Decorator)는 클래스와 멤버에 메타데이터를 추가하거나 동작을 확장할 수 있는 고급 기능입니다.
- 제네릭 클래스는 다양한 타입의 데이터를 처리할 수 있어, 재사용성과 타입 안전성을 동시에 제공합니다.
- 클래스는 타입과 값의 두 가지 역할을 하므로, 타입스크립트에서 클래스 이름은 타입 선언과 값(생성자 함수) 모두로 동작합니다.
주요 포인트 (Key Points)
- 타입스크립트 클래스는 ES2015 JavaScript 클래스에 몇 가지 타입 전용 확장을 추가하며, 런타임 추가 사항이 하나 또는 두 개 있습니다.
클래스 생성 (Creating a Class Instance)
- 클래스 인스턴스를 생성할 때는
new
키워드를 사용합니다.
class ABC {...}
const abc = new ABC();
private vs #private
private
는 타입 전용 추가이며, 런타임에 영향을 주지 않습니다. 클래스 외부에서 항목에 접근할 수 있습니다.
class Bag {
private item: any; // 타입 전용 private
}
const a: Bag = new Bag();
a.item; // 접근 가능
#private
는 런타임에서만 접근 가능한 private로, JavaScript 엔진 내에서 클래스 내부에서만 접근할 수 있도록 강제합니다.
class #Bag {
#item: any; // 런타임 private
}
const a: Bag = new Bag();
a.#item; // 접근 불가, 오류 발생
‘this’ in Classes
- 클래스 내 함수에서
this
의 값은 함수가 호출되는 방식에 따라 달라집니다. 다른 언어에서 클래스 인스턴스가 항상this
로 사용되는 것과는 다릅니다. this
매개변수,bind
함수, 또는 화살표 함수를 사용하여 문제를 해결할 수 있습니다.
class MyClass {
constructor(public name: string) {}
printName = () => {
console.log(this.name); // 화살표 함수로 'this'를 클래스 인스턴스로 유지
}
}
const myInstance = new MyClass("Hello");
myInstance.printName(); // "Hello"
Type and Value
- 클래스는 타입(type)과 값(value)으로 모두 사용할 수 있습니다. 즉, 클래스 이름은 타입 선언과 값(생성자 함수) 모두로 동작합니다.
- 예시:
const a: Bag = new Bag(); // Bag은 타입(좌측)과 값(우측) 모두로 사용됨
- 하지만, 클래스와 인터페이스의 차이로 인해 아래와 같은 코드는 피해야 합니다.
class C implements Bag {}
// C는 Bag 인터페이스(혹은 클래스)의 구조만 따르지만, Bag 클래스의 인스턴스는 아님
const bag: Bag = new C(); // 오류 발생: C는 Bag의 인스턴스가 아님
- 왜 오류? C는 Bag의 구조(예: 메서드나 속성)를 복사하지만, new C()로 만든 객체는 Bag 클래스의 진짜 인스턴스가 아니에요. TypeScript는 인스턴스의 ‘출신’을 확인하거든요. 마치 “이 가방은 Bag 브랜드가 아니야”라고 보는 거죠.
- 피하는 방법: 만약 Bag을 인터페이스로 사용하려면, 처음부터 interface Bag {}으로 선언하세요. 클래스로 선언했다면 상속(extends)을 고려해보세요.
- 타입스크립트(Korean: 타입스크립트, English: TypeScript)에서 클래스는 타입과 값의 두 가지 역할을 하므로, 타입과 값의 구분을 명확히 이해해야 합니다.
주요 클래스 문법 (Common Syntax)
- 클래스 선언, 상속, 인터페이스 구현 등 기본적인 클래스 문법을 사용합니다.
// Account 클래스를 상속(subclass)
// Updatable, Serializable 인터페이스를 구현(implements)
class User extends Account implements Updatable, Serializable {
id: string; // 필드(멤버 변수)
displayName?: string; // 선택적 필드
name!: string; // "무조건 있음"을 컴파일러에 알림(Non-null 단언)
#attributes: Map<any, any> = new Map(); // #으로 시작하는 필드는 런타임 private(진짜 비공개)
roles = ["user"]; // 기본값이 있는 필드
readonly createdAt = new Date(); // 읽기 전용 필드(생성 시 값 고정)
constructor(id: string, email: string) { // 생성자
super(id); // 부모 클래스 생성자 호출
this.email = email; // 필드 초기화
// ...
}
setName(name: string) { this.name = name; } // 일반 메서드
verifyName = (name: string) => { /* ... */ }; // 화살표 함수 메서드(필드로 선언)
// 오버로드(Overload) 예시: 같은 이름, 다른 시그니처
sync(): Promise<{ ... }>;
sync(cb: ((result: string) => void)): void;
sync(cb?: ((result: string) => void)): void | Promise<{ ... }> {
if (cb) {
// 콜백 방식
} else {
return new Promise((resolve) => {
// 프로미스 방식
});
}
// Getter/Setter
get accountID() { /* ... */ }
set accountID(value: string) { /* ... */ }
private makeRequest() { /* ... */ } // private: 클래스 내부에서만 접근 가능
protected handleRequest() { /* ... */ } // protected: 자신과 하위 클래스에서만 접근 가능
// static(정적) 필드/메서드: 클래스 자체에 속함, 인스턴스가 아닌 클래스명으로 접근
static #userCount = 0; // 정적 private 필드
static registerUser(user: User) { /* ... */ }
static {
this.#userCount = -1; // static 블록: 정적 필드 초기화 등
}
}
제네릭 (Generics)
- 클래스에서 제네릭을 사용하여 타입을 동적으로 정의할 수 있습니다.
class Box<Type> {
contents: Type; // 제네릭 타입 필드
constructor(value: Type) {
this.contents = value; // 생성자에서 제네릭 타입 초기화
}
}
const stringBox = new Box<string>("Hello"); // string 타입 박스
const numberBox = new Box<number>(42); // number 타입 박스
파라미터 프로퍼티 (Parameter Properties)
- 생성자 매개변수에
public
,private
,protected
키워드를 사용하여 자동으로 클래스 필드를 생성할 수 있습니다.
class Location {
constructor(public x: number, public y: number) {} // x, y 필드
}
const loc = new Location(20, 40);
console.log(loc.x); // 20
console.log(loc.y); // 40
추상 클래스 (Abstract Classes)
- 추상 클래스는 인스턴스를 만들 수 없지만, 하위 클래스에서 구현해야 하는 메서드를 정의할 수 있습니다.
abstract class Animal {
abstract getName(): string; // 추상 메서드
printName() {
console.log("Hello, " + this.getName()); // 추상 메서드 호출
}
}
class Dog extends Animal {
getName(): string {
return "Dog"; // 추상 메서드 구현
}
}
const dog = new Dog();
dog.printName(); // "Hello, Dog"
데코레이터와 속성 (Decorators and Attributes)
- 클래스, 메서드, 접근자, 속성 및 메서드 매개변수에 데코레이터를 사용할 수 있습니다.
import { Syncable, triggersSync, preferCache, required } from "mylib";
@Syncable
class User {
@triggersSync()
save() { /* ... */ }
@preferCache(false)
get displayName() { /* ... */ }
@required
update(@info: Partial<User>) { /* ... */ } // 매개변수 데코레이터
}
인터페이스(Interface)
추가 설명
- 인터페이스는 객체의 구조(Shape)를 정의하며, 클래스, 객체, 함수 등 다양한 곳에 적용할 수 있습니다.
- 인터페이스는 여러 번 선언하면 자동으로 병합(Merging)되어, 확장성과 유연성이 뛰어납니다.
- 인터페이스는 선택적 속성, 읽기 전용 속성, 인덱스 시그니처 등 다양한 문법을 지원합니다.
- 클래스는 implements 키워드로 인터페이스를 구현(Conformance)할 수 있습니다.
- 인터페이스는 타입 시스템에서 계약(Contract) 역할을 하여, 코드의 일관성과 유지보수성을 높여줍니다.
- 인터페이스는 타입 별칭과 달리, 선언적 확장이 가능하여 여러 라이브러리나 모듈에서 구조를 쉽게 합칠 수 있습니다.
주요 포인트 (Key Points)
- 객체의 모양을 설명하는 데 사용되며, 다른 인터페이스나 타입으로 확장될 수 있습니다.
- JavaScript의 런타임 동작에 맞게 설계되어 있습니다. JavaScript의 거의 모든 것은 객체이며, 인터페이스는 이러한 런타임 동작에 맞게 설계되었습니다.
built-in 타입 프리미티브 (Built-in Type Primitives)
boolean
,string
,number
,undefined
,null
,any
,unknown
,never
,void
,bigint
,symbol
일반적인 내장 JS 객체 (Common Built-in JS Objects)
Date
,Error
,Array
,Map
,Set
,Regexp
,Promise
타입 리터럴 (Type Literals)
- 객체:
{ field: string }
- 함수:
(arg: number) => string
- 배열:
string[]
또는Array<string>
- 튜플:
[string, number]
피해야 할 것들 (Avoid)
Object
,String
,Number
,Boolean
(이들은 일반적으로 사용하지 않는 것이 좋습니다. 대신, 구체적인 타입을 사용하세요.)
일반 구문 (Common Syntax)
interface JSONResponse extends Response, HTTPAble {
version: number; // 필드
/** In bytes */ // JSDoc 주석, 에디터에 표시됨
payloadSize: number; // 바이트 단위 페이로드 크기
outOfStock?: boolean; // 이 속성은 객체에 없을 수도 있음
// 함수 속성, 두 가지 방법으로 설명 가능
update: (retryTimes: number) => void;
update: (retryTimes: number): void;
(): JSONResponse; // 이 객체를 ()로 호출할 수 있음
new (s: string): JSONResponse; // 이 인터페이스가 설명하는 객체를 new로 사용할 수 있음
[key: string]: number; // 이 인터페이스에 설명되지 않은 모든 속성은 숫자여야 함
readonly body: string; // 이 속성은 변경할 수 없음을 TypeScript에 알림
}
제네릭 (Generics)
- 타입 매개변수를 사용하여 인터페이스에서 변경 가능한 타입을 선언할 수 있습니다.
interface APICall<Response> {
data: Response; // 제네릭 타입 필드
}
// 사용 예시
const api: APICall<ArtworkCall> = { data: ... };
api.data; // ArtworkCall 타입 데이터 접근
- 제네릭 매개변수에
extends
키워드를 사용하여 어떤 타입이 허용되는지 제약을 설정할 수 있습니다.
interface APICall<Response extends { status: number }> {
data: Response; // status 속성이 있는 타입만 허용
}
const api: APICall<ArtworkCall> = { data: { status: 200, ... } };
api.data.status; // status 속성에 접근
오버로드 (Overloads)
- 호출 가능한 인터페이스는 서로 다른 매개변수 세트에 대해 여러 정의를 가질 수 있습니다.
interface Expect {
(matcher: boolean): string; // boolean 매처에 대한 정의
(matcher: string): boolean; // 문자열 매처에 대한 정의
}
Get & Set
- 객체는 사용자 정의 getter 또는 setter를 가질 수 있습니다.
interface Ruler {
get size(): number; // 크기 getter
set size(value: number | string); // 크기 setter, 숫자 또는 문자열 허용
}
// 사용 예시
const r: Ruler = { size: 12 }; // 크기 설정
r.size = "36"; // 문자열로 크기 설정
확장(Extension) via Merging
- 인터페이스는 병합되므로, 여러 선언이 타입 정의에 새 필드를 추가합니다.
interface APICall<Response> {
data: Response; // 기본 데이터 필드
}
interface APICall<Response> {
error?: Error; // 선택적 오류 필드 추가
}
클래스 일치 (Class Conformance)
- 클래스가 인터페이스를 준수하는지 확인할 수 있습니다.
interface Syncable {
sync(): void; // 동기화 메서드
}
class Account implements Syncable {
sync() {
// 동기화 구현
}
}
제어 흐름 분석(Control Flow Analysis)
추가 설명
- 제어 흐름 분석(CFA)은 코드의 실행 경로에 따라 변수의 타입을 자동으로 좁혀주는 기능입니다.
- if, switch, typeof, instanceof, in, 사용자 정의 타입 가드 등 다양한 방법으로 타입을 좁힐 수 있습니다.
- 타입 가드(Type Guard)와 어설션 함수(Assertion Function)는 복잡한 유니온 타입을 안전하게 분기 처리할 때 유용합니다.
- as const를 사용하면 객체와 배열의 모든 속성을 리터럴 타입으로 고정할 수 있습니다.
- 제어 흐름 분석은 타입스크립트의 강력한 타입 추론(Type Inference) 기능의 핵심입니다.
- 타입스크립트는 코드의 흐름을 분석하여, 변수의 타입을 상황에 따라 자동으로 좁혀주기 때문에, 안전하고 직관적인 코딩이 가능합니다.
주요 포인트 (Key Points)
- CFA(제어 흐름 분석)는 거의 항상 유니온 타입을 받아 코드의 논리에 따라 유니온 내부의 타입 수를 줄여줍니다.
- 대부분의 경우 CFA는 자연스러운 JavaScript 불리언 논리 내에서 동작하지만, TypeScript가 타입을 좁히는 방식에 영향을 주는 사용자 정의 함수를 만들 수도 있습니다.
If Statements
타입이 좁혀지는 대부분의 경우는 if 문 내부의 표현식에서 발생하며, 다양한 타입 연산자가 새로운 스코프에서 타입을 좁혀줍니다.
// - `typeof` (원시 타입에 대해)
const input = getUserInput();
input // string | number
if (typeof input === "string") {
input; // string
}
// - `instanceof` (클래스에 대해),
const input = getUserInput();
input // number | number[]
if (input instanceof Array) {
input; // number[]
}
// - "property" in object (객체에 대해)
const input = getUserInput();
input // string | { error: ... }
if ("error" in input) {
input; // { error: ... }
}
// - type-guard 함수(for anything)
const input = getUserInput();
input // number | number[]
if (Array.isArray(input)) {
input; // number[]
}
Expressions
- 타입 좁히기는 코드와 함께 발생하며, 불리언 연산을 수행할 때도 좁혀집니다.
const input = getUserInput();
input // string | number
const inputLength = (typeof input === "string" && input.length) || input; // input: string
Discriminated Unions
- 모든 유니온 멤버가 동일한 속성 이름을 가지고 있다면, CFA는 해당 속성을 기준으로 타입을 구분할 수 있습니다.
type Responses =
| { status: 200, data: any }
| { status: 301, to: string }
| { status: 400, error: Error };
- 사용법
const response = getResponse();
response // Responses
switch(response.status) {
case 200: return response.data; // data
case 301: return redirect(response.to); // to
case 400: return response.error; // error
}
타입 가드(Type Guards)
- 타입 가드는 특정 타입인지 확인하는 함수로, TypeScript가 해당 타입으로 좁힐 수 있도록 도와줍니다.
function isErrorResponse(obj: Response): obj is APIErrorResponse {
return obj instanceof APIErrorResponse; // APIErrorResponse 타입인지 확인
}
- 사용법
const response = getResponse();
response // Response | APIErrorResponse
if (isErrorResponse(response)) {
response; // APIErrorResponse
}
Assertion Functions
- 어설션 함수는 현재 스코프를 변경하거나 예외를 던지는 함수로, TypeScript가 타입을 좁히는 데 사용됩니다.
function assertResponse(obj: any): asserts obj is SuccessResponse {
if (!(obj instanceof SuccessResponse)) {
throw new Error("Not a success!"); // 예외 발생
}
}
- 사용법
const response = getResponse();
response // SuccessResponse | ErrorResponse
assertResponse(response); // 어설션 함수는 현재 스코프를 변경하거나 예외를 발생시킵니다.
response; // SuccessResponse
Assignment
- 객체의 하위 필드는 변경 가능한(mutable) 것으로 간주되며, 할당 시 타입이 리터럴(literal) 타입에서 더 넓은(widened) 타입으로 확장됩니다.
as const
접두사를 사용하면 모든 타입이 리터럴 타입으로 고정됩니다.
const data1 = { name: "Zagreus" }; // typeof data1 = { name: string }
const data2 = { name: "Zagreus" as const }; // typeof data2 = { name: "Zagreus" }; // 리터럴 타입으로 고정
- Tracks through related variables
const response = getResponse();
const isSuccessResponse = res instanceof SuccessResponse; // 현재 스코프의 타입 좁히기
if (isSuccessResponse) {
res.data; // SuccessResponse
}
- Reassignment updates types
let data: string | number = ...; // 초기 타입
data; // string | number
data = "Hello"; // 재할당 후 타입 업데이트
data; // string