본 도구에서 사용하는 오픈소스 라이브러리

본 도구의 코드에는 1개의 오픈소스 라이브러리가 포함되어 있습니다.

TypeScript 치트시트

TypeScript 5 문법, 타입 시스템과 가장 자주 쓰는 관용 표현의 빠른 참조 매뉴얼로, 일상 시나리오의 약 80%를 다룹니다.

TS

TypeScript TypeScript 5.x

ECMAScript + 타입 · 다중 패러다임 · 구조적 · 정적(점진적) 타입 · JS 기반

추천 학습 경로

먼저 컴파일 환경(tsc/tsconfig)을 설정하고 TS가 JS의 상위 집합임을 이해함 → 기본 타입 표기, 유니온 타입과 인터페이스 마스터 → 타입 시스템 심화(제네릭, 타입 가드, 타입 조작) → class와 모듈로 코드 조직화 → 비동기와 DOM/Node 타입 처리 → 마지막으로 필요에 따라 빌드 설정, 테스트, 디버깅을 조회. FAQ 섹션은 나중에 돌아와 함정을 피할 때 적합함.

1.Hello World와 빌드 환경

TypeScript를 빌드하고 실행하며, tsc, tsconfig, 그리고 타입 검사 흐름을 이해한다.

최소 프로그램

TS는 JS의 상위 집합이다. 합법적인 JS는 모두 합법적인 TS이며, 타입 표기를 추가한 뒤 tsc로 컴파일하면 JS가 된다.

1
2
3
4
5
6
// hello.ts
const message: string = 'Hello, world!';
console.log(message);
// 型注釈:変数名の後の : string
// $ npx tsc hello.ts # コンパイルして hello.js を生成
// $ node hello.js

tsc 컴파일

tsc는 .ts를 .js로 컴파일한다. --noEmit은 타입 검사만 수행하고 출력하지 않으며, --watch는 변경을 감시하고, --strict는 엄격 모드다.

1
2
3
4
5
// $ npx tsc app.ts # 単一ファイルをコンパイル
// $ npx tsc --noEmit # 型チェックのみ
// $ npx tsc --watch # 監視して自動コンパイル
// $ npx tsc --strict # 厳格な型チェック
// $ npx tsc --outDir dist # 出力ディレクトリ

tsconfig.json

tsconfig.json에서 컴파일을 설정한다. target, module, strict, outDir 등을 지정하며, npx tsc는 이를 자동으로 읽는다.

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true, // 厳格モード
"outDir": "dist",
"sourceMap": true // デバッグ用ソースマップ
},
"include": ["src"]
}
// tsc --init でデフォルト設定を生成

TS 직접 실행

tsx/ts-node을 사용하면 TS를 별도 컴파일 없이 바로 실행할 수 있다. Node 22는 --experimental-strip-types를 네이티브로 지원한다.

1
2
3
4
5
// $ npx tsx script.ts # TS を直接実行
// $ npx ts-node script.ts # 旧方式
// Node 22+:
// $ node --experimental-strip-types script.ts
// 開発スクリプトは tsx が一般的、本番は tsc でコンパイルして実行

엄격 모드

strict: true는 모든 엄격 검사를 활성화한다. null 검사, 암묵적 any에 대한 오류, 사용하지 않는 변수에 대한 경고 등이 포함된다. 새 프로젝트에서는 반드시 켜야 한다.

1
2
3
4
5
6
7
8
// strict には以下が含まれます:
// 1. strictNullChecks null を通常の型に代入不可
// 2. noImplicitAny 引数に型がない場合にエラー
// 3. noUnusedLocals 未使用の変数でエラー
function greet(name: string) { // 明示的な型
return 'Hi ' + name;
}
// strict を有効にしない場合、name は暗黙の any となりエラーにならない

TS와 JS의 관계

TS는 컴파일 시점에 타입을 검사하며, 컴파일된 JS에는 타입 정보가 없다. 타입은 컴파일 시점에만 존재하고, 런타임에는 사라진다.

1
2
3
4
5
6
7
8
interface User {
name: string;
age: number;
}
const u: User = { name: 'Nick', age: 30 };
// コンパイル結果(型は削除される):
// const u = { name: 'Nick', age: 30 };
// 型エラーはコンパイル時に検出され、実行には影響しない

의존성과 타입 패키지

라이브러리에는 타입 정의가 필요하다. @types/*는 DefinitelyTyped 커뮤니티 타입 패키지이며, typescript는 컴파일 도구 의존성이다.

1
2
3
4
5
6
// $ npm i -D typescript @types/node
// $ npm i -D @types/express # コミュニティ型
// ライブラリに付属する型:
// axios は直接 export するため、@types 不要
// // @ts-ignore コメント:次の行のチェックをスキップ(慎重に)
// import axios from 'axios'; 型定義あり

에디터 통합

VS Code는 TS를 기본 지원한다. 호버로 타입을 확인하고, 오류는 빨간 밑줄로 표시되며, 자동 완성 및 리팩토링을 제공한다. 오류 메시지는 그대로 표시된다.

1
2
3
4
5
6
7
// VS Code ショートカット:
// ホバー:型を表示
// クイック修正:Ctrl+. (Cmd+.)
// シンボルの名前変更:F2
// 定義へジャンプ:F12
// 型エラーは Problems パネルに表示
// 設定:ワークスペースの tsconfig には `tsc` のバージョンを合わせる必要あり

2.변수와 타입 표기

타입 표기, 타입 추론, 유니온 타입, any/unknown 그리고 타입 단언.

타입 표기

변수명 뒤에 : 타입 형식으로 타입을 표기한다. 표기 후 타입이 고정되며, 다른 타입을 대입하면 컴파일 오류가 발생한다.

1
2
3
4
5
6
const name: string = 'Nick';
let age: number = 30;
const isAdmin: boolean = true;
// エラー例:
// age = 'thirty'; // 型エラー
// 初期化後は型推論が効くため明示不要

타입 추론

TS는 초기화 값으로부터 타입을 추론하므로 대부분의 경우 명시적 표기가 필요 없다. 복잡한 타입은 명시적 인터페이스를 권장한다.

1
2
3
4
5
6
7
8
const name = 'Nick'; // string と推論
let count = 42; // number
const arr = [1, 2, 3]; // number[]
const obj = { a: 1 }; // { a: number }
// 代入時のユニオン推論:
let status = 'idle' as const;
// 複雑なシナリオでは明示注釈が分かりやすい:
let data: Map<string, User> = new Map();

유니온 타입

| 유니온 타입은 "또는"을 나타낸다. 예를 들어 string | number는 둘 다 허용한다. 멤버에 접근하려면 먼저 타입 가드가 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
function print(value: string | number) {
// ユニオン型は共通メンバーのみアクセス可
console.log(value.toString());
// 分岐処理:
if (typeof value === 'string') {
console.log(value.toUpperCase()); // string
} else {
console.log(value.toFixed(2)); // number
}
}
// リテラルユニオン:
type Dir = 'up' | 'down' | 'left' | 'right';

any와 unknown

any는 타입 검사를 비활성화하므로(피해야 함), unknown은 미지의 값을 나타낸다(가드 후에만 사용 가능). 외부 데이터를 안전하게 처리할 때는 unknown을 사용한다.

1
2
3
4
5
6
7
8
9
let risky: any = 'text'; // any:チェックなし、慎重に
risky.method(); // コンパイルは通るがランタイムでクラッシュの可能性
// unknown:安全
let data: unknown = getApi();
if (typeof data === 'string') {
console.log(data.length); // ガード後に使用可
}
// unknown のアサーション:data as string
// any より unknown を優先

타입 단언

as 단언은 컴파일러보다 자신이 더 잘 안다고 선언하는 것이다. 단언은 런타임을 바꾸지 않으며, 컴파일 시점의 선언일 뿐이다. 남용하면 오류를 가린다.

1
2
3
4
5
6
7
const el = document.getElementById('btn') as HTMLButtonElement;
// 二重アサーションは非推奨(落とし穴):
// const n = value as unknown as number;
// より安全:まずガード、それからアサーション
const json = JSON.parse(text) as User[];
// as const:リテラルへの絞り込み
const modes = ['dev', 'prod'] as const;

non-null 단언

! 접미사는 값이 null/undefined가 아님을 단언한다. 확실할 때만 사용해야 하며, 그렇지 않으면 런타임에 충돌할 수 있다.

1
2
3
4
5
6
7
8
9
let name: string | null = getMaybe();
const len = name!.length; // 非 null アサーション(リスクあり)
// より安全な書き方:
if (name) {
const l2 = name.length;
}
// または null 合体:
const l3 = name?.length ?? 0;
// 非 null アサーションはコンパイル時の約束、ランタイムで null だとクラッシュ

리터럴 타입

리터럴 타입은 타입을 구체적인 값으로 한정한다. 예를 들어 'up', 42, true 등이 있다. 유니온과 함께 사용해 옵션을 열거한다.

1
2
3
4
5
6
7
8
let direction: 'up' | 'down' = 'up';
// direction = 'sideways'; // エラー:ユニオンに含まれない
const yes: true = true;
// オブジェクトプロパティの絞り込み:
const config = {
mode: 'production',
} as const; // mode: 'production' リテラル
// as const でオブジェクトメンバーを読み取り専用リテラル型に

구조 분해와 타입

구조 분해 할당에서도 타입이 유지된다. 함수 매개변수를 구조 분해할 때는 매개변수 객체 전체의 타입을 표기해야 한다.

1
2
3
4
5
6
7
8
9
10
interface User { name: string; age: number; }
const { name, age }: User = getUser();
// 関数の引数で分割代入:
function show({ name, age }: User) {
console.log(name, age);
}
// 配列の分割代入:
const [first, second] = [1, 2] as const;
// オプションプロパティの分割代入:
const { name = 'guest' }: { name?: string } = data;

3.타입 시스템

기본 타입, 객체/배열/튜플, 인터페이스, 제네릭, 타입 별칭 그리고 타입 조작.

기본 타입

string, number, boolean, null, undefined, void, symbol, bigint로 구성된 기본 타입 집합이다.

1
2
3
4
5
6
7
8
const s: string = 'text';
const n: number = 42;
const b: boolean = true;
const v: void = undefined; // 戻り値なしの関数
const nl: null = null;
const u: undefined = undefined;
const sym: symbol = Symbol('id');
const big: bigint = 10n;

배열과 튜플

number[]는 배열, [string, number]는 튜플(고정 길이·고정 순서), readonly는 읽기 전용 배열이다.

1
2
3
4
5
6
7
8
9
const nums: number[] = [1, 2, 3];
const strs: Array<string> = ['a', 'b']; // ジェネリック構文
// タプル:
let pair: [string, number] = ['age', 30];
// pair[0] = 42; // エラー:型が一致しない
// 読み取り専用配列:
const fixed: readonly number[] = [1, 2];
// fixed.push(3); // エラー:読み取り専用
// オプショナルタプル要素:type T = [string, number?]

객체 타입

객체 타입은 형태를 묘사한다. 속성, 선택적 ?, 읽기 전용 readonly, 메서드 시그니처 등을 포함한다.

1
2
3
4
5
6
7
8
9
10
11
12
interface Point {
readonly x: number; // 読み取り専用
y: number;
label?: string; // オプショナル
}
const p: Point = { x: 1, y: 2 };
// p.x = 10; // エラー:readonly
// メソッド:
interface Greeter {
greet(name: string): string;
// または greet: (name: string) => string;
}

interface와 type

interface는 객체 형태를 정의하며(확장 가능), type 별칭은 더 유연하다(유니온/교차/튜플). 일반적으로는 interface를 우선 사용한다.

1
2
3
4
5
6
7
8
9
10
11
interface User {
name: string;
}
// interface はマージ/継承可能:
interface Admin extends User {
permissions: string[];
}
// type エイリアス:
type ID = string | number; // ユニオン
type Pair = [string, number]; // タプル
type Shape = { area: number } & { color: string }; // 交差

열거형

enum은 명명된 상수 집합이다. 숫자 열거형, 문자열 열거형, const enum 등이 있으며, 문자열 열거형이 더 자주 쓰인다.

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Color {
Red, // 0
Green, // 1
Blue, // 2
}
enum Status {
Active = 'active',
Inactive = 'inactive',
}
const c: Color = Color.Green;
const s: string = Status.Active; // 'active'
// 逆引きマッピング:Color[0] === 'Red'(数値 enum)
// 型だけ欲しい場合:type S = 'active' | 'inactive'

제네릭

제네릭은 타입을 매개변수화한다. T는 타입 매개변수이며, 함수/클래스/인터페이스를 범용적으로 재사용할 수 있게 하고 컴파일 시점에 결정된다.

1
2
3
4
5
6
7
8
9
10
11
function identity<T>(value: T): T {
return value;
}
const s = identity('hello'); // string
const n = identity(42); // number
// ジェネリック interface:
interface Box<T> {
value: T;
}
const box: Box<number> = { value: 42 };
// 複数パラメータ:function pair<A, B>(a: A, b: B)

keyof와 인덱스

keyof는 객체 키의 유니온을 얻고, T[K]는 인덱스 접근이며, 매핑 타입은 타입 조작의 핵심 도구다.

1
2
3
4
5
6
7
8
9
10
interface User { name: string; age: number; }
type Keys = keyof User; // 'name' | 'age'
// インデックスアクセス:
type NameType = User['name']; // string
// マップ型:
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// Partial<T>、Required<T>、Pick<T,K> は組み込みユーティリティ
// 一部オプショナル:type PartialUser = Partial<User>

내장 유틸리티 타입

Partial, Omit, Pick, Record, Exclude, ReturnType 등의 매핑 타입이 일반적인 변환을 단순화한다.

1
2
3
4
5
6
7
type PartialUser = Partial<User>; // すべてオプショナル
type PickName = Pick<User, 'name'>; // name のみ取得
type NoAge = Omit<User, 'age'>; // age を除外
type Rec = Record<string, number>; // キーと値のマッピング
type WithoutZero = Exclude<0 | 1 | 2, 0>; // 1 | 2
type R = ReturnType<typeof fn>; // 関数の戻り型
// Parameters<typeof fn> は引数の型

템플릿 리터럴 타입

템플릿 문자열 문법으로 문자열 타입을 구성한다. 유니온과 결합하여 순열 조합을 생성할 수 있으며, 문자열 파싱을 타입 수준에서 구현하는 기법이다.

1
2
3
4
5
6
7
8
type Event = `on${'Click' | 'Hover'}`;
// Event = 'onClick' | 'onHover'
type Size = `${'small' | 'large'}-${number}`;
// Size = 'small-1' | 'large-2' ...
// 文字列からの抽出:
// type Extracted = 'a:b'.split<'a:b', ':'>; // 型レベルの split
// 簡単な文字列パース:
// type First = 'abc' extends `${infer F}bc` ? F : never; // 'a'

4.타입 가드와 null 값

타입 가드, 타입 좁히기, 옵셔널 체이닝, null 처리 그리고 참조 의미.

타입 가드

typeof, instanceof, in으로 유니온 타입을 좁힌다. 분기 내부에서 타입이 자동으로 좁아진다.

1
2
3
4
5
6
7
8
9
10
11
function f(v: string | number | Date) {
if (typeof v === 'string') {
v.toUpperCase(); // string
} else if (v instanceof Date) {
v.getTime(); // Date
} else {
v.toFixed(2); // number
}
}
// in ガード:
if ('permissions' in user) { /* Admin */ }

타입 좁히기

가드 이후 타입은 해당 범위 내에서 좁아진다(narrowing). null 검사나 truthy 검사 모두 타입을 좁힌다.

1
2
3
4
5
6
7
8
9
10
11
let value: string | null = getMaybe();
if (value) {
value.length; // string(絞り込み)
}
// 真偽値での絞り込み:
function f(s: string | undefined) {
s ?? console.log('missing'); // null 合体での絞り込み
}
// null チェック後:
value = null;
if (value === null) return; // 以降 value は非 null

식별 유니온

식별 유니온은 공통 판별 필드(kind/type)를 공유하며, switch 이후 타입이 정확하게 좁아진다. 상태 모델링에 적합하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number };
function area(s: Shape): number {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'square': return s.side * s.side;
// 網羅性チェック:
default: {
const _exhaustive: never = s;
return _exhaustive;
}
}
}

옵셔널 체이닝과 nullish 병합

?.는 안전한 접근, ??는 nullish에 대한 기본값, ??=는 기본값 할당이다. 체인 형태의 깊은 접근으로 null로 인한 충돌을 방지한다.

1
2
3
4
5
6
7
8
9
const name = user?.profile?.name ?? 'guest';
const count = data?.items?.length ?? 0;
// null 合体代入:
let settings = getConfig();
settings ??= { theme: 'dark' };
// オプショナル呼び出し:
callback?.();
// ?? と || の違い:
// 0 ?? 'x' は 0;0 || 'x' は 'x'

null과 undefined

strictNullChecks가 켜져 있으면 null/undefined는 일반 타입에 대입할 수 없다. 명시적 유니온이나 명시적 처리가 필요하다.

1
2
3
4
5
6
7
8
9
let name: string | null = null; // ユニオンに null を含む
let title: string | undefined;
// 関数の戻り値が null 可能性あり:
function find(): User | null {
return Math.random() > 0.5 ? null : { name: 'x' };
}
const u = find();
if (u) { u.name; } // 絞り込み後にアクセス
// 安全なアサーション:u!.name または u ?? { name: '?' }

참조 의미

TS는 JS의 참조 의미를 바꾸지 않는다. 객체는 참조로 공유되고 배열은 얕은 복사된다. 타입은 그 형태만 기술할 뿐이다.

1
2
3
4
5
6
7
8
9
const a = { x: 1 };
const b = a; // 参照共有
b.x = 99;
console.log(a.x); // 99
// コピーしてもシャローコピー:
const c = { ...a };
c.x = 1; // a.x は不変
// TS の型は不変性を保証しない(readonly 必要)
// 深い凍結は as const + ライブラリで

사용자 정의 타입 가드

is 문법으로 함수가 타입 가드임을 선언한다. boolean을 반환하며 매개변수의 타입을 좁힌다. 배열 필터링에 자주 쓰인다.

1
2
3
4
5
6
7
8
9
function isString(v: unknown): v is string {
return typeof v === 'string';
}
const values: unknown[] = ['a', 1, 'b', null];
const strs = values.filter(isString);
// strs の型は string[](ガードが効く)
// アロー関数での書き方:
const s2 = values.filter(
(v): v is string => typeof v === 'string');

단언 함수

asserts 단언 함수는 불변 조건을 선언한다. 반환 타입은 void이지만 호출 후 타입이 좁아지며, 오류를 던지면 실행이 중단된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function assertString(v: unknown): asserts v is string {
if (typeof v !== 'string') {
throw new Error('期望 string');
}
}
function process(v: unknown): void {
assertString(v);
v.toUpperCase(); // string に絞り込まれる
}
// 無条件アサーション:
function assert(cond: unknown): asserts cond {}
// 値のアサーション:
function assertNonNull<T>(v: T): asserts v is NonNullable<T> {}
// ランタイム不変条件 + 型絞り込みの二重保険

5.제어 흐름

분기, 반복, switch와 타입 좁히기의 조합.

if / else

if/else 분기에 타입 가드를 결합한다. 조건식이 변수의 타입을 좁힌다.

1
2
3
4
5
6
7
8
9
function describe(v: string | number) {
if (typeof v === 'string') {
return `字符串: ${v.toUpperCase()}`;
} else {
return `数字: ${v.toFixed(2)}`;
}
}
// 多分岐 else-if + ガードで段階的に絞り込み
// truthy チェック:if (value) で非空に絞り込み

switch와 완전성 검사

switch는 식별 유니온을 처리한다. default 분기에서 never를 사용해 모든 경우가 처리되었는지 검사할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
type Action =
| { type: 'add'; n: number }
| { type: 'reset' };
function reducer(a: Action) {
switch (a.type) {
case 'add': return a.n;
case 'reset': return 0;
default: {
const exhaustive: never = a; // 分岐不足はコンパイルエラー
return exhaustive;
}
}
}

반복문

for, for...of, while로 순회한다. 배열은 for...of로 반복하고, 인덱스가 필요하면 for 또는 entries를 사용한다.

1
2
3
4
5
6
7
8
9
for (let i = 0; i < 10; i++) { }
for (const item of items) { }
for (const [i, v] of items.entries()) {
console.log(i, v);
}
let n = 0;
while (n < 5) { n++; }
// オブジェクトキーの反復:
for (const key of Object.keys(obj) as (keyof typeof obj)[]) { }

삼항 연산자와 타입

삼항 연산자의 양쪽 분기 타입이 유니온된다. 조건에 따라 다른 타입이 반환되면 결과는 유니온 타입이 된다.

1
2
3
4
5
6
const result = cond ? 'yes' : 0;
// result の型は 'yes' | 0(リテラルユニオン)
// 型を統一したい場合は明示注釈:
const msg: string = cond ? 'yes' : String(0);
// ネストした三項は可読性が悪いのでガードを使う
// null 値:v ?? fallback

break와 continue

continue는 현재 반복을 건너뛰고, break는 종료하며, 레이블이 있는 break/continue는 중첩을 제어합니다.

1
2
3
4
5
6
7
8
9
10
11
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) continue;
if (i > 7) break;
}
outer:
for (const a of list) {
for (const b of a.items) {
if (b.done) continue outer;
}
}
// 型は変わらず、純粋な制御フロー

조기 반환

가드 절로 일찍 return하면 중첩이 줄어듭니다. null 체크 후에는 타입이 좁혀집니다.

1
2
3
4
5
6
7
8
function process(u: User | null) {
if (u === null) return; // 早期リターン
if (u.age < 18) return;
console.log(u.name); // 非 null に絞り込まれている
}
// 複数のガードでメインロジックを平坦に
// null 合体で早期にデフォルト値を:
const name = u?.name ?? 'guest';

do...while

do...while은 본문을 먼저 한 번 실행한 뒤 조건을 검사합니다. 최소 한 번은 실행해야 하는 반복에 씁니다. 타입은 관여하지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
let attempts = 0;
do {
attempts++;
const ok = tryOnce();
if (ok) break;
} while (attempts < 3);
// 最低 1 回実行し、その後条件を判定
// while:先に判定してから実行(0 回もあり得る)
// 使用シーン:
// リトライ、メニュー選択、入力バリデーション
// 無限ループに注意:条件は最終的に false になる必要あり

객체 순회

Object.keys는 객체의 키를 순회합니다. 타입 단언에 keyof를 쓰면 안전합니다. 값을 순회할 때는 Object.values를 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
const config = { host: 'x', port: 3000 };
// キーの反復(アサーション必要):
for (const key of Object.keys(config) as (keyof typeof config)[]) {
console.log(key, config[key]);
}
// 値の反復:
for (const value of Object.values(config)) { }
// キーと値のペア:
for (const [k, v] of Object.entries(config)) { }
// 型の注意点:
// Object.keys は string[] を返すため、アサーション後に安全にアクセス

6.함수

함수 타입 표기, 선택/기본 매개변수, 오버로드, rest 매개변수, this.

함수 타입

함수 타입 표기: 매개변수 타입과 반환 타입. 화살표 함수의 타입과 함수 선언문은 동등합니다.

1
2
3
4
5
6
7
8
9
10
11
// 関数宣言:
function add(a: number, b: number): number {
return a + b;
}
// アロー関数:
const add = (a: number, b: number): number => a + b;
// 関数型の変数:
type Fn = (a: number, b: number) => number;
const f: Fn = add;
// void 戻り値:
function log(msg: string): void { console.log(msg); }

선택 매개변수와 기본 매개변수

?는 선택 매개변수, =는 기본 매개변수입니다. 기본 매개변수는 암묵적으로 선택입니다. 선택 매개변수는 필수 매개변수 뒤에 와야 합니다.

1
2
3
4
5
6
7
8
9
10
11
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
// デフォルト引数:
function mul(a: number, b = 2): number {
return a * b;
}
mul(3); // 6
// デフォルト引数は省略可能
// オプショナル引数は後ろに配置:
// greet('Nick', undefined) も可

rest 매개변수

...rest는 개수가 정해지지 않은 매개변수를 배열로 수집합니다. rest 매개변수는 배열 타입으로 표기해야 합니다.

1
2
3
4
5
6
7
8
9
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
// ジェネリック rest でタプルを保持:
function tuple<T extends unknown[]>(...args: T): T {
return args;
}
const t = tuple(1, 'a', true); // [number, string, boolean]

오버로드 시그니처

오버로드: 여러 시그니처 선언 + 하나의 구현. 호출 시 시그니처와 매칭됩니다. 매개변수 조합에 따라 반환 타입을 제약합니다.

1
2
3
4
5
6
7
8
function pick(obj: Record<string, unknown>, key: string): unknown;
function pick(obj: number[], index: number): number;
function pick(obj: any, key: string | number): unknown {
return obj[key];
}
// 実装シグネチャは外部から見えない
// オーバーロードは順にマッチ、緩い型は最後に
// 戻り値型が異なるケース:DOM API 型でよく使われる

제네릭 함수

제네릭 매개변수 제약: T extends 제약조건. 제약으로 타입을 제한한 뒤 그 제약 타입의 멤버를 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
function first<T extends string | number[]>(arr: T): T[number] {
return arr[0];
}
const s = first('hello'); // string
const n = first([1, 2, 3]); // number
// 制約付き呼び出し:
function getLen<T extends { length: number }>(v: T): number {
return v.length;
}
// 複数ジェネリック:<K, V extends keyof K>

this 타입

this 매개변수로 this 타입을 표기합니다. 메서드 체인은 this를 반환해 체이닝을 구현합니다. 화살표 함수는 this를 바인딩하지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Builder {
private items: string[] = [];
add(item: string): this {
this.items.push(item);
return this; // チェーン
}
}
const b = new Builder().add('a').add('b');
// 明示的な this パラメータ(先頭に):
function log(this: { name: string }) {
console.log(this.name);
}
// アロー関数は外側の this を継承

콜백과 함수 매개변수

콜백 함수를 매개변수로 사용: 함수 타입으로 표기합니다. 배열 고차 함수 map/filter/reduce의 타입 추론.

1
2
3
4
5
6
7
8
9
function withLog(fn: (n: number) => number) {
return fn(42);
}
withLog(n => n * 2); // 引数型は自動推論
// 配列の高階関数:
const doubled = [1, 2, 3].map(n => n * 2);
const evens = [1, 2, 3, 4].filter(n => n % 2 === 0);
const total = [1, 2, 3].reduce((acc, n) => acc + n, 0);
// コールバックの this 型に注意、ロストしないように

함수 제약 팁

매개변수 유니언 좁히기, 선택적 콜백, 반환 추론. 함수 매개변수에는 구체적인 클래스보다 인터페이스를 쓰는 편이 낫습니다.

1
2
3
4
5
6
7
8
9
10
function handle(v: string | number, cb?: (r: string) => void) {
const r = typeof v === 'string' ? v.toUpperCase() : String(v);
cb?.(r); // オプショナルコールバック
}
// 引数には interface(構造的型):
interface HasId { id: number }
function findById<T extends HasId>(arr: T[], id: number): T | undefined {
return arr.find(x => x.id === id);
}
// 構造的型:形状が合えばよく、同一インスタンスでなくてよい

7.문자열

템플릿 문자열, 자주 쓰는 메서드, 정규식과 문자 처리.

템플릿 문자열

백틱 템플릿 문자열: ${}로 보간하고 여러 줄을 그대로 유지합니다. 타입은 여전히 string입니다.

1
2
3
4
5
6
7
8
9
10
11
12
const name = 'Nick';
const greeting = `Hello, ${name}!`;
// 複数行:
const lines = `
line 1
line 2
`;
// 式:
const total = `Sum: ${1 + 2}`;
// テンプレートリテラル型:
// type T = `id-${string}`
// 型は依然として string、テンプレートはシンタックスシュガー

자주 쓰는 메서드

slice/substring으로 부분 문자열을 잘라내고, toUpperCase로 변환하며, split으로 나누고, includes/startsWith로 조회하며, replace로 교체합니다.

1
2
3
4
5
6
7
8
9
const s = 'TypeScript';
const sub = s.slice(0, 4); // 'Type'
const upper = s.toUpperCase(); // 'TYPESCRIPT'
const parts = s.split(''); // 文字配列
s.includes('Script'); // true
s.startsWith('Type'); // true
s.endsWith('t'); // true
const r = 'a-b-c'.replace(/-/g, '_'); // 'a_b_c'
const padded = '7'.padStart(3, '0'); // '007'

템플릿 리터럴 타입

타입 수준 템플릿 문자열: ${}로 타입을 이어 붙입니다. infer로 문자열 구조를 추출해 추론합니다.

1
2
3
4
5
6
7
8
type Route = `/users/${string}/profile`;
const good: Route = '/users/123/profile';
// 型レベルの抽出:
type ExtractId<S extends string> =
S extends `/users/${infer Id}/profile` ? Id : never;
type Id = ExtractId<'/users/42/profile'>; // '42'
// 大文字変換:Uppercase<T>、Lowercase<T>
// 組み合わせ:type All = `${'a'|'b'}${'1'|'2'}` // 'a1'|'a2'|'b1'|'b2'

문자와 코드 포인트

length는 UTF-16 코드 유닛 수입니다(이모지는 둘로 계산됨). 코드 포인트를 순회할 때는 for...of 또는 Array.from을 사용합니다.

1
2
3
4
5
6
7
8
9
const emoji = '👋';
console.log(emoji.length); // 2(サロゲートペア)
console.log([...emoji].length); // 1
const s = 'abc';
for (const ch of s) { } // コードポイント単位
// コードポイントアクセス:
const first = Array.from(s)[0];
// charCodeAt/fromCodePoint でコードポイントを処理:
String.fromCodePoint(128075); // '👋'

정규식

RegExp로 매칭과 교체를 합니다. match는 캡처 그룹 배열을 반환하고, matchAll은 전역 순회합니다. 타입 수준에서 RegExp는 고정되어 있습니다.

1
2
3
4
5
6
7
8
9
10
11
const re = /(\w+)@(\w+)/g;
const all = s.matchAll(re);
for (const m of all) {
console.log(m[1], m[2]); // キャプチャグループ
}
// キャプチャ付き置換:
const r = '2026-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');
// 非 null アサート:
const match = s.match(/(\w+)@/);
if (match) { console.log(match[1]); }

로케일과 비교

localeCompare로 로케일 비교, toLocaleLowerCase로 로케일 변환, Intl로 숫자/날짜 포맷을 지정합니다.

1
2
3
4
5
6
const arr = ['ä', 'a', 'z'].sort((a, b) => a.localeCompare(b, 'zh'));
const num = (1234.5).toLocaleString('zh-CN'); // '1,234.5'
const date = new Date().toLocaleDateString('zh-CN');
// Intl.NumberFormat:
const nf = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' });
// ロケール一覧:Intl.supportedValuesOf('language')

이스케이프 문자

문자열 이스케이프: \n 줄바꿈, \t 탭, \\ 백슬래시, \u 코드 포인트. 작은따옴표와 큰따옴표 모두에서 이스케이프합니다.

1
2
3
4
5
6
7
8
9
10
const nl = '第一行\n第二行';
const tab = 'a\tb'; // a スペースタブ b
const backslash = 'C:\\path'; // C:\path
const quote = 'He said \'hi\'';
const uni = '\u4e2d'; // '中'
const code = '\u{1F600}'; // emoji コードポイント
// テンプレート文字列はシングルクォート/ダブルクォートのエスケープ不要
const tmpl = `She said "hi" and 'bye'`;
// よく使う:\n 改行 \t インデント \\ パス
// JSON 出力ではクォートをエスケープする必要あり

뒤집기와 비교

문자열을 뒤집을 때는 split과 배열을 사용하고, 공백을 제거하며, 중복을 없앱니다. 비교에는 localeCompare 또는 정규화된 비교를 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
const s = 'hello';
// 反転:
const rev = [...s].reverse().join(''); // 'olleh'
// 先頭末尾の空白を除去:
const t = ' text '.trim();
// すべての空白を除去:
const compact = s.replace(/\s+/g, '');
// 繰り返し:'ab'.repeat(3) // 'ababab'
// 回文判定:
const isPalindrome = s === [...s].reverse().join('');
// 正規化比較(大文字小文字を無視):
s.toLowerCase() === 'HELLO'.toLowerCase()

8.컬렉션과 객체

배열, 객체, Map/Set과 불변 업데이트 패턴.

배열 연산

push/pop은 끝쪽, unshift/shift는 앞쪽, splice는 추가/삭제, slice는 복사, includes/indexOf는 조회.

1
2
3
4
5
6
7
8
9
const arr = [1, 2, 3];
arr.push(4); // [1,2,3,4]
const last = arr.pop(); // 4
arr.unshift(0); // [0,1,2,3]
const first = arr.shift(); // 0
const removed = arr.splice(1, 1); // 削除
const copy = arr.slice(); // シャローコピー
arr.includes(2);
arr.indexOf(2); // 最初の位置または -1

map / filter / reduce

함수형 순회: map으로 변환, filter로 거르고, reduce로 집계, find로 찾고, every/some로 판정. 새 배열을 반환하며 원본 배열은 바꾸지 않습니다.

1
2
3
4
5
6
7
8
9
const nums = [1, 2, 3, 4];
const doubled = nums.map(n => n * 2);
const evens = nums.filter(n => n % 2 === 0);
const sum = nums.reduce((acc, n) => acc + n, 0);
const found = nums.find(n => n > 2); // 3 | undefined
const ok = nums.every(n => n > 0); // true
const has = nums.some(n => n === 4); // true
// チェーン:
nums.filter(n => n % 2).map(n => n * 10).reduce((a, b) => a + b, 0);

객체 연산

스프레드로 합치기 {...a, ...b}, Object.keys/values/entries로 순회, keyof로 타입의 키.

1
2
3
4
5
6
7
8
9
10
const base = { id: 1, name: 'Nick' };
const extended = { ...base, age: 30 }; // マージ
const override = { ...base, name: 'New' }; // 上書き
const keys = Object.keys(base);
// 型安全な反復:
for (const key of Object.keys(base) as (keyof typeof base)[]) {
console.log(base[key]);
}
// entries:
for (const [k, v] of Object.entries(base)) { }

Map

Map은 임의 키 매핑: set/get/has/delete/size. 삽입 순서를 유지하고 O(1) 조회.

1
2
3
4
5
6
7
8
9
10
11
const scores = new Map<string, number>();
scores.set('alice', 90);
const v = scores.get('alice'); // number | undefined
scores.has('bob'); // false
scores.delete('alice');
scores.size;
// 反復:
for (const [k, val] of scores) { }
for (const key of scores.keys()) { }
// 初期化:
new Map([['a', 1], ['b', 2]]);

Set

Set은 중복 없는 집합: add/delete/has/size. 배열의 중복을 제거하거나 차집합/합집합을 구할 때 사용합니다.

1
2
3
4
5
6
7
8
9
10
const set = new Set<number>();
set.add(1).add(2).add(1); // {1, 2}
set.has(1); // true
set.delete(2);
// 配列の重複排除:
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]
// 和集合:
const union = new Set([...a, ...b]);
// 積集合:
const inter = new Set([...a].filter(x => bSet.has(x)));

불변 업데이트

제자리 변경 대신 스프레드/복사로 업데이트합니다. 객체 교체, 배열 추가/삭제는 새 참조를 반환합니다. React 상태에서 흔히 쓰입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface State {
items: string[];
count: number;
}
const next: State = {
...state,
items: [...state.items, 'new'], // 追加
count: state.count + 1,
};
// 1 件削除:
const filtered = state.items.filter(x => x !== 'old');
// 1 件更新:
const updated = state.items.map((x, i) => i === 0 ? 'new' : x);
// readonly で型チェックによる in-place 変更防止

튜플과 Record

튜플은 고정 길이, Record는 키-값 매핑입니다. 객체 리터럴에 as const를 붙여 상수로 변환합니다.

1
2
3
4
5
6
7
8
9
10
11
const point: [number, number] = [10, 20];
const [x, y] = point; // 分割代入
// Record:
type Config = Record<string, boolean>;
const flags: Config = { debug: true };
// as const 定数オブジェクト:
const statusMap = {
active: '运行中',
stopped: '已停止',
} as const;
// statusMap.active の型は '运行中' リテラル

WeakMap / WeakSet

WeakMap/WeakSet의 키는 반드시 객체여야 하며 약한 참조입니다. GC를 막지 않습니다. 메타데이터 캐싱이나 부작용용으로 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const meta = new WeakMap<object, { visited: boolean }>();
const node = document.getElementById('a');
if (node) meta.set(node, { visited: true });
// キーはオブジェクトのみ:
// meta.set(1, {}) // エラー
// 反復不可(keys/size なし)
// 用途:
// 1. オブジェクトにプライベートメタデータを紐付ける
// 2. 計算結果をキャッシュしてリークを防ぐ
// 3. リスナー用フラグ
// WeakSet は集合のマーキング用:
const processed = new WeakSet<object>();
processed.add(obj);
processed.has(obj); // true

9.성능과 메모리

가비지 컬렉션 아래의 메모리 관점, 대용량 데이터, 문자열 최적화와 모니터링.

가비지 컬렉션

TS/JS는 GC가 있어 수동 해제가 필요 없습니다. 더 이상 참조되지 않는 객체는 회수됩니다. 긴 수명을 가진 객체는 클로저가 붙잡고 있지 않도록 주의하세요.

1
2
3
4
5
6
7
8
9
10
// オブジェクトはスコープ外かつ参照なしで GC される
function create() {
const big = new Array(1e6);
return () => big.length; // クロージャが big を保持
}
// クロージャが big を生かしている:
const f = create(); // big は解放不可
// 解放:
f = null; // 参照を切る
// グローバルキャッシュが無限に膨らむのを避ける

대용량 배열

대용량 배열 연산에는 TypedArray나 스트리밍을 고려하세요. filter/map은 새 배열을 만들어 비용이 발생합니다. 반복문에서 재사용하는 편이 더 경제적입니다.

1
2
3
4
5
6
7
8
9
10
// TypedArray でバイナリ処理:
const buf = new Float64Array(1e6);
// 大きな配列で map/filter の連鎖を避ける:
// BAD:複数パス
// GOOD:1 パスで処理
const src = new Array(1e6).fill(0);
let sum = 0;
for (let i = 0; i < src.length; i++) sum += src[i];
// バイナリ:DataView + ArrayBuffer
// 数値精度:BigInt で大整数、BigInt.asIntN で切り詰め

문자열 메모리

문자열은 불변이며 이어 붙이면 새 문자열이 만들어집니다. 대량의 연결은 배열 join이나 템플릿을 사용하세요. 문자열 인터닝(interning).

1
2
3
4
5
6
7
8
9
// ループでの文字列連結は遅い:
let s = '';
for (let i = 0; i < 1e5; i++) s += i; // BAD
// 配列の join を使う:
const parts: string[] = [];
for (let i = 0; i < 1e5; i++) parts.push(String(i));
const s2 = parts.join(''); // GOOD
// またはテンプレート文字列で分割
// 長い文字列のスライスは slice(O(n))

WeakRef와 캐시

WeakRef는 약한 참조라 가비지 컬렉션을 막지 않고, WeakMap/WeakSet은 키가 약하게 참조됩니다. 캐시나 부작용 추적에 약한 컨테이너를 사용하세요.

1
2
3
4
5
6
7
8
9
const cache = new WeakMap<object, number>();
const obj = { id: 1 };
cache.set(obj, compute(obj));
// obj が GC されるとエントリも自動で消える
// WeakRef:
const ref = new WeakRef(obj);
const alive = ref.deref(); // object | undefined
// FinalizationRegistry で回収を監視:
const reg = new FinalizationRegistry(held => console.log('collected', held));

성능 팁

any 사용은 최적화를 늦추므로 피하고, 재할당을 줄이며, 결과를 캐시합니다. V8 최적화는 안정적인 형태의 객체에 의존합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
// 動的プロパティでオブジェクト形状を変えない:
// BAD:
const obj: Record<string, number> = {};
obj.a = 1; obj.b = 2; // 形状変化
// GOOD:完全な形状を宣言
const obj = { a: 0, b: 0 };
// 長いチェーンをキャッシュ:
const len = arr.length; // ループ内で再取得しない
// 暗黙の型変換を避ける:
const s = String(n) + x;
// ホットパスではクロージャ生成を避ける:
for (let i = 0; i < n; i++) { }
// map で毎回アロー関数を生成しない

메모리 모니터링

Node에서 메모리를 확인할 때는 process.memoryUsage(), --max-old-space-size를 사용합니다. 브라우저에서는 Performance API를 사용합니다.

1
2
3
4
5
6
7
8
9
// Node:
console.log(process.memoryUsage());
// heapUsed 使用済みヒープ、heapTotal 総ヒープ
// ヒープを増やす:node --max-old-space-size=4096 app.js
// ブラウザ:
performance.measureMemory?.()
.then(m => console.log(m.bytes));
// ヒープスナップショット:Chrome DevTools の Memory パネル
// リークの特徴:heapUsed が下がり続けることなく増加

클로저와 메모리

클로저는 외부 변수를 붙잡아 그 수명을 연장합니다. 반복문 안의 클로저 함정에 주의하세요. 참조를 해제합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function makeCounter() {
let count = 0; // クロージャにキャプチャされる
return () => ++count; // 生存する
}
const c = makeCounter();
c(); // 1
// ループ内クロージャキャプチャ(var の罠):
// BAD:var は共有される
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 3 3 3
}
// GOOD:let はブロックスコープでキャプチャ
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 0 1 2
}
// 長寿命クロージャの解放:
// fn = null

TypedArray와 바이너리

TypedArray는 바이너리 수치를 다룹니다: Uint8Array/Float64Array. 뷰는 내부 buffer를 공유합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const bytes = new Uint8Array(16); // 16 バイト
bytes[0] = 255;
// 既存データから作成:
const arr = new Uint8Array([1, 2, 3]);
// 浮動小数:
const floats = new Float64Array(8);
// バッファ共有:
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setInt32(0, 42);
view.getInt32(0); // 42
// 通常の配列に変換:
const plain = Array.from(bytes);
// エンコード:
new TextEncoder().encode('中文');
// 大容量ファイル/ネットワークプロトコル/Canvas ピクセルで頻出

10.클래스와 객체 지향

class, 접근 제어자, 상속, 추상 클래스, 제네릭 클래스.

class 기본

class 문법: 필드, 생성자, 메서드. 필드는 타입과 가시성을 표기할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hi, I'm ${this.name}`;
}
}
const p = new Person('Nick', 30);
// パラメータプロパティ shorthand:
class P2 {
constructor(public name: string, private age: number) {}
}

접근 제어자

public은 공개, private은 비공개, protected는 보호, readonly는 읽기 전용입니다. 모두 컴파일 시점에 검사됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
class Account {
public owner: string; // デフォルト public
private balance = 0; // プライベート
protected type = 'basic'; // サブクラスからアクセス可
readonly id: string; // 読み取り専用
constructor(owner: string) {
this.owner = owner;
this.id = crypto.randomUUID();
}
}
// # プライベートフィールド(実行時プライベート、ES2022):
class C { #secret = 1; get() { return this.#secret; } }

상속과 override

extends로 상속, super()로 부모 생성자 호출, override로 부모 메서드 재정의. 자식은 부모의 일종입니다(is-a).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal {
constructor(public name: string) {}
speak(): string { return `${this.name} makes a sound`; }
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name); // まず親コンストラクタを呼ぶ
}
override speak(): string { // override を明示
return `${this.name} barks`;
}
}
const d = new Dog('Rex', 'Husky');
// d は Dog であり Animal でもある

추상 클래스와 인터페이스

abstract 클래스는 인스턴스화할 수 없으며, 추상 메서드는 자식이 반드시 구현해야 합니다. 인터페이스는 모양을 제약합니다. 추상 클래스는 구현을 가질 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Shape {
abstract area(): number; // サブクラスは必ず実装
describe(): string {
return `Area: ${this.area()}`;
}
}
class Circle extends Shape {
constructor(private r: number) { super(); }
override area(): number { return Math.PI * this.r ** 2; }
}
// interface 制約:
interface HasArea { area(): number }
function printArea(s: HasArea) { console.log(s.area()); }

implements

class implements 인터페이스: 클래스는 인터페이스의 모양을 충족해야 합니다. 한 클래스가 여러 인터페이스를 구현할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
interface Runnable {
run(): void;
}
interface Jumpable {
jump(): void;
}
class Player implements Runnable, Jumpable {
run(): void { console.log('running'); }
jump(): void { console.log('jump'); }
}
// メソッド不足はコンパイルエラー
// implements は形の制約のみ、継承関係を要求しない

제네릭 클래스

제네릭 클래스: 타입 매개변수는 필드와 메서드에 사용된다. 제약을 통해 제네릭의 범위를 한정한다.

1
2
3
4
5
6
7
8
9
10
11
class Box<T> {
private value: T;
constructor(value: T) { this.value = value; }
get(): T { return this.value; }
set(v: T): void { this.value = v; }
}
const numBox = new Box<number>(42);
const strBox = new Box('hi'); // string と推論
// 静的メンバーは型パラメータを参照できない:
// static arr: T[] // エラー
// ジェネリック制約:class Box<T extends { id: number }>

getter / setter

get/set 접근자가 필드의 읽기/쓰기를 감싼다. 검증과 계산 로직을 추가할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Temperature {
private _celsius = 0;
get celsius(): number { return this._celsius; }
set celsius(value: number) {
if (value < -273.15) throw new Error('绝对零度以下');
this._celsius = value;
}
get fahrenheit(): number {
return this._celsius * 9 / 5 + 32;
}
}
const t = new Temperature();
t.celsius = 25;
console.log(t.fahrenheit); // 77

정적 멤버

static은 클래스 수준의 필드와 메서드를 정의한다. 정적 멤버는 인스턴스에 의존하지 않는다. 팩토리 메서드와 상수에 static을 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MathHelper {
static readonly PI = 3.14159; // クラス定数
static max(arr: number[]): number {
return Math.max(...arr);
}
// ファクトリーメソッド:
static create(name: string): MathHelper {
return new MathHelper(name);
}
constructor(private name: string) {}
}
MathHelper.PI;
MathHelper.max([1, 5, 3]);
// 静的メンバーはクラス名でアクセス:
// new MathHelper().PI // エラー
// 静的プロパティ初期化はクラス定義時に実行される

11.예외 처리

throw/try-catch, 오류 타입, 사용자 정의 오류, 비동기 오류.

throw / try-catch

throw로 오류를 던지고, try-catch로 잡고, finally로 마무리한다. catch 변수는 기본적으로 unknown이므로 좁혀야 한다.

1
2
3
4
5
6
7
8
9
10
11
try {
const n = JSON.parse(text);
if (typeof n !== 'number') throw new Error('需要数字');
} catch (err) {
if (err instanceof Error) {
console.log(err.message); // 絞り込み後にアクセス
}
} finally {
cleanup(); // 成否に関わらず実行
}
// キャッチしないと上位へ伝播、未処理だとクラッシュ

사용자 정의 오류

extends Error로 사용자 정의 오류 클래스를 만든다. 추가 정보를 함께 담는다. 오류 이름은 타입을 구분하는 데 쓰인다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ValidationError extends Error {
constructor(public field: string, message: string) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('email', '邮箱格式错误');
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.field, err.message);
}
}
// プロトタイプチェーンを確実に(instanceof のため):
// Object.setPrototypeOf(this, ValidationError.prototype)

일반적인 오류 타입

Error 베이스 클래스: TypeError 타입 오류, RangeError 범위 초과, ReferenceError 참조 오류. 판별은 instanceof를 쓴다.

1
2
3
4
5
6
7
8
9
10
11
12
try {
// TypeError: 存在しないメソッド呼び出し
// RangeError: 配列範囲外 / 再帰が深すぎる
// ReferenceError: 未宣言変数の参照
const arr = [1, 2];
arr[5].toFixed(); // TypeError
} catch (err) {
if (err instanceof TypeError) { }
else if (err instanceof RangeError) { }
// instanceof Error でフォールバック
}
// クロス realm では name 属性で判定

비동기 오류

async 함수의 throw는 rejected Promise가 된다. await 시 try-catch로 잡는다. Promise.catch 체이닝.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function load(): Promise<void> {
try {
const res = await fetch('/api');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
} catch (err) {
console.error('加载失败', err);
}
}
// Promise チェーン:
fetch('/api').then(r => r.json()).catch(e => {
console.error(e);
});
// 未処理の rejected Promise は unhandledrejection をトリガ

오류 경계

모듈 경계에서 오류를 잡아 변환한다. 서드파티 오류는 통일해 감싼다. 오류가 빠져나가면 크래시가 발생한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function safeParse<T>(json: string): T {
try {
return JSON.parse(json) as T;
} catch {
throw new Error('JSON 解析失败'); // 統一的にラップ
}
}
// エントリでキャッチ:
process.on('uncaughtException', (err) => {
console.error('未捕获异常', err);
process.exit(1);
});
// Node では同期例外をトップレベルでフォールバック
// ブラウザ:window.onerror / unhandledrejection

오류 처리 패턴

예측 가능한 오류는 결과로 반환하고, 예상치 못한 오류는 예외를 던진다. Result 스타일(ok/err)과 throw는 각각의 쓰임이 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 予想可能な失敗:null / Result オブジェクトを返す
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
function parseNum(s: string): Result<number> {
const n = Number(s);
return Number.isNaN(n)
? { ok: false, error: '不是数字' }
: { ok: true, value: n };
}
const r = parseNum('abc');
if (r.ok) console.log(r.value);
else console.log(r.error);
// 予想外の失敗:throw + 上位でキャッチ
// ルール:呼び出し側が処理できるなら返す、そうでなければ throw
// 握り潰さない:catch 後は少なくとも log

오류 메시지 품질

오류 메시지에는 맥락이 포함된다: 무엇이, 어디서, 어떻게 고치는지. 사용자 정의 오류는 필드를 함께 담는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class HttpError extends Error {
constructor(
public status: number,
public url: string,
message: string,
) {
super(message);
this.name = 'HttpError';
}
}
// GOOD:コンテキスト付き
throw new HttpError(404, url, `资源不存在: ${url}`);
// BAD:情報不足
// throw new Error('失败了');
// ログにスタックを含める:
console.error(err); // stack を保持
// エラー原因チェーン:
new Error('外层失败', { cause: innerErr })

처리되지 않은 거부

잡히지 않은 rejected Promise는 unhandledrejection을 발생시킨다. 최상위에서 안전망을 둬 조용한 실패를 방지한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Node:
process.on('unhandledRejection', (reason) => {
console.error('未处理的 Promise 拒绝', reason);
});
// ブラウザ:
window.addEventListener('unhandledrejection', (e) => {
e.preventDefault();
console.error('未处理拒绝', e.reason);
});
// 同期例外:
process.on('uncaughtException', (err) => {
console.error('未捕获异常', err);
process.exit(1);
});
// 監査:テストでリスナーを張って取りこぼし検知
// フォールバックは握り潰しではなく、ログと特定

12.입출력

console 출력, Node 파일/스트림, fetch 네트워크, 타입이 지정된 JSON.

console 출력

console.log/info/warn/error, 템플릿 출력, %o 포맷, 그룹과 카운트.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log('Hello');
console.info('info 消息');
console.warn('警告');
console.error('错误');
// フォーマット:
console.log('%o', { a: 1 }); // オブジェクト展開
console.table([{ a: 1 }, { a: 2 }]);
// グループ:
console.group('组');
console.log('内容');
console.groupEnd();
// 時間計測:
console.time('t');
console.timeEnd('t');

fetch 네트워크

fetch는 비동기 요청이다. 응답을 await하고, json을 파싱하며, 오류를 처리하고, 결과를 타입 단언한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function getUsers(): Promise<User[]> {
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<User[]>; // アサーション
}
// POST:
await fetch('/api', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Nick' }),
});
// AbortController でタイムアウト:
const ac = new AbortController();
setTimeout(() => ac.abort(), 3000);
await fetch(url, { signal: ac.signal });

Node 파일

fs/promises로 비동기 읽기/쓰기. readFile/writeFile/mkdir/readdir은 Promise를 반환한다.

1
2
3
4
5
6
7
8
9
10
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
const text = await readFile('data.txt', 'utf8');
await writeFile('out.txt', text.toUpperCase());
await mkdir('dir', { recursive: true });
const files = await readdir('.');
// 大容量ファイルのストリーム:
import { createReadStream } from 'node:fs';
const stream = createReadStream('big.log');
// @types/node が必要:
// npm i -D @types/node

JSON과 타입

JSON.parse/stringify 직렬화. parse 결과는 가드로 검증이 필요하고, stringify는 함수를 무시한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
interface User { name: string; age: number }
const u: User = { name: 'Nick', age: 30 };
const json = JSON.stringify(u);
// パース後にバリデーション:
function isUser(v: unknown): v is User {
return typeof v === 'object' && v !== null
&& typeof (v as any).name === 'string'
&& typeof (v as any).age === 'number';
}
const data = JSON.parse(json);
if (isUser(data)) console.log(data.name);
// JSON.parse は any を返すためアサート前にガード推奨
// シリアライズオプション:JSON.stringify(u, null, 2) で整形

스트리밍 처리

ReadableStream/Web Streams로 대용량 응답을 처리한다. 진행 상황을 읽고, 청크 단위로 처리한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
const res = await fetch('/big');
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let total = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
total += value.length;
const chunk = decoder.decode(value, { stream: true });
process(chunk);
}
// 大容量ファイルをストリーム読み込み:
// for await (const chunk of createReadStream('big.log')) { }

환경 변수와 인자

Node에서 process.argv / process.env를 읽는다. 인자 파싱, 환경 변수 타입 좁히기.

1
2
3
4
5
6
7
8
9
10
11
// process.argv[0]=node, [1]=スクリプト, [2..] 引数
const args = process.argv.slice(2);
// 環境変数:
const port = Number(process.env.PORT ?? 3000);
const mode = process.env.NODE_ENV ?? 'development';
// 判定:
if (mode === 'production') { }
// .env 読み込み:
// npm i dotenv
// import 'dotenv/config'
// 型安全な環境:process.env は Record<string, string | undefined>

브라우저 Web API

localStorage/sessionStorage, navigator, WebSocket의 타입. 저장 값은 직렬화가 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ローカルストレージ:
localStorage.setItem('token', 'abc');
const t = localStorage.getItem('token'); // string | null
// オブジェクト保存にはシリアライズが必要:
localStorage.setItem('user', JSON.stringify(user));
// 読み戻しはパース + 検証:
const raw = localStorage.getItem('user');
if (raw) { const u = JSON.parse(raw) as User; }
// ナビゲーション:
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition((pos) => {
console.log(pos.coords.latitude);
});
}
// WebSocket イベント型:
ws.addEventListener('message', (e: MessageEvent) => {
console.log(e.data);
});

파일 읽기

input[type=file]로 File을 얻고, FileReader로 텍스트/DataURL을 읽으며, 객체 URL로 미리 본다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const input = document.querySelector('input[type=file]') as HTMLInputElement;
input.addEventListener('change', async () => {
const file = input.files?.[0];
if (!file) return;
// テキスト読み込み:
const text = await file.text();
// DataURL として読み込み:
const reader = new FileReader();
reader.onload = () => console.log(reader.result);
reader.readAsDataURL(file);
// 画像プレビュー:
const url = URL.createObjectURL(file);
img.src = url;
// File には name/size/type がある
console.log(file.name, file.size, file.type);
});

13.흔한 실수

일상 개발에서 가장 자주 겪는 함정과 올바른 작성법.

any 남용

any는 타입 검사를 꺼 실제 오류를 가린다. unknown + 가드가 가능하면 그쪽을 쓴다.

1
2
3
4
5
6
7
8
9
10
// BAD:any がエラーを握り潰す
function getLen(v: any): number {
return v.length; // コンパイル通過、ランタイムでクラッシュの可能性
}
// GOOD:unknown + ガード
function getLen(v: unknown): number {
if (typeof v !== 'string') return 0;
return v.length;
}
// any の暗黙伝播:戻り値 any が呼び出し側を汚染

빈 값 판별

!!로 truthy 판별, == null로 null/undefined 동시 판별, 배열의 빈 판별. 0이나 ''를 빈 값으로 취급하지 않는다.

1
2
3
4
5
6
7
8
9
10
// BAD:値が 0 や '' の可能性あり
if (value) { } // 0 は falsy
// GOOD:明示的に null を判定
if (value !== null && value !== undefined) { }
// 簡略 == null で両方判定:
if (value == null) { } // null または undefined
// 配列が空の判定:
if (arr.length === 0) { }
// オブジェクトが空の判定:
if (Object.keys(obj).length === 0) { }

async 오용

async 함수는 반드시 Promise를 반환한다. await를 잊으면 Promise가 누출된다. forEach는 async 대기를 지원하지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
// BAD:forEach は待たない
async function main() {
[1, 2, 3].forEach(async n => { await work(n); });
console.log('done'); // 先に表示される!
}
// GOOD:for...of
for (const n of [1, 2, 3]) {
await work(n);
}
// BAD:await を忘れる
const res = fetch('/api'); // Promise のまま
// 並列:
await Promise.all([a(), b()]);

동등 비교

===는 엄격한 동등이다. NaN !== NaN. 객체는 참조로 비교한다. 깊은 비교는 직접 작성하거나 라이브러리를 쓴다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BAD:== の緩い比較は型変換される
'1' == 1; // true、落とし穴
// GOOD:=== で厳密比較
'1' === 1; // false
// NaN は特殊:
NaN === NaN; // false
Number.isNaN(NaN); // true
// オブジェクトは参照で比較:
{} === {}; // false
// 配列内容の比較:
[1, 2].join() === [1, 2].join(); // 簡易版
// 深い比較ライブラリ:
// import { isEqual } from 'lodash-es';
// パフォーマンス:深いオブジェクトの頻繁な比較を避ける

null과 undefined 혼동

null은 의도적인 빈 값이고, undefined는 미할당이다. strictNullChecks에서 따로 처리한다. 옵셔널 체이닝 대 단언.

1
2
3
4
5
6
7
8
9
10
11
12
// BAD:アサーションが null を見えなくする
const len = name!.length; // name が実際に null かもしれない
// GOOD:先に判定
if (name) {
const len = name.length;
}
// null 合体:
const n = name ?? 'default';
// オプショナルチェーンで安全アクセス:
user?.profile?.email;
// ?? を使う(|| ではなく):0 や '' は有効値
const port = port ?? 3000; // port=0 のとき 0 を保持

this 손실

콜백에서 this는 undefined가 된다. 화살표 함수로 바인딩하거나 명시적으로 바인딩한다. 클래스 필드에서 화살표 함수가 자주 쓰인다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Counter {
count = 0;
increment = () => { this.count++; }; // アローで束縛
}
// BAD:コールバックで this が失われる
class C {
value = 1;
method() { return this.value; }
}
const fn = new C().method;
fn(); // undefined エラー
// GOOD:
const fn2 = new C().method.bind(new C());
// または呼び出し時に .method() 形式で

좁히기 실패

속성에 접근한 후에는 좁히기가 사라진다. 분해할당 후에야 좁혀진다. 함수 매개변수를 재할당하면 타입이 넓어진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// BAD:プロパティの絞り込みが保たれない
function f(p: { a?: string }) {
if (p.a) {
p.a.toUpperCase(); // 既に変化している可能性
}
}
// GOOD:一旦変数に保存
function f2(p: { a?: string }) {
const a = p.a;
if (a) {
a.toUpperCase();
}
}
// オプショナルプロパティを 2 回参照すると競合の可能性
// 引数を再代入すると絞り込みが失われる:
// let x = v; その後判定

완전 검사

구별 가능한 유니온의 default에서 never로 검사한다. 새 타입 분기를 누락하면 컴파일 오류가 난다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// GOOD:default で never を使い網羅性チェック
type Event =
| { kind: 'open' }
| { kind: 'close' };
function handle(e: Event) {
switch (e.kind) {
case 'open': break;
case 'close': break;
default: {
const _exhaustive: never = e;
// 新しい kind 追加時ここでコンパイルエラー
return _exhaustive;
}
}
}
// BAD:default を付けない、新規型は黙って未処理
// 型を更新するとコンパイラが全 switch をチェック

복사와 변경

배열/객체 참조 공유로 잘못 변경될 수 있다. 업데이트할 때는 복사한다. sort는 배열을 제자리에서 바꾼다.

1
2
3
4
5
6
7
8
9
10
11
12
13
const a = [1, 2, 3];
// BAD:sort は in-place
const sorted = a.sort(); // a も変わる
// GOOD:先にコピー
const sorted = [...a].sort();
// BAD:参照共有による誤変更
const b = a;
b.push(4); // a も変わる
// シャローコピー:
const copy = [...a];
// オブジェクトスプレッドでシャローコピー:
const o2 = { ...o1 };
// ネストした深いコピーは層ごとに処理が必要

14.동시성과 비동기

Promise, async/await, Worker 스레드와 이벤트 루프.

Promise

Promise는 비동기 결과를 나타낸다. then 체이닝, catch 오류, finally 마무리. 타입은 Promise<T>로 표기한다.

1
2
3
4
5
6
7
8
9
10
const p: Promise<number> = new Promise((resolve, reject) => {
setTimeout(() => resolve(42), 1000);
});
p.then(n => console.log(n))
.catch(err => console.error(err))
.finally(() => console.log('done'));
// 即時作成:
Promise.resolve(1);
Promise.reject(new Error('x'));
// ジェネリクス:Promise<T> の成功値型は T

async / await

async 함수는 Promise를 반환한다. await가 풀어준다. 오류는 try-catch로 다룬다. 최상위 await에는 ESM이 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
async function load(): Promise<User> {
const res = await fetch('/api/user');
if (!res.ok) throw new Error('加载失败');
return res.json() as Promise<User>;
}
// 呼び出し:
const user = await load();
// エラー:
try { await load(); } catch (err) { }
// 並列実行:
const [a, b] = await Promise.all([load(), load()]);
// トップレベル await(ESM .mjs)

병렬과 경합

Promise.all은 전부 완료, allSettled은 전부(실패 포함), race는 가장 먼저 완료, any는 첫 성공.

1
2
3
4
5
6
7
8
9
10
11
const tasks = [fetch1(), fetch2(), fetch3()];
// 全部成功(1 つでも失敗で全体失敗):
const all = await Promise.all(tasks);
// 中断せず全結果を収集:
const settled = await Promise.allSettled(tasks);
// 最初に完了したもの(失敗含む):
const first = await Promise.race(tasks);
// 最初に成功したもの(全部失敗で AggregateError):
const any = await Promise.any(tasks);
// 並列数制限:
// for ループで分割、または p-limit ライブラリ

Worker 스레드

Web Worker / Node worker_threads로 병렬 계산. postMessage 통신, transferable로 소유권을 이전한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// メインスレッド:
const worker = new Worker('/worker.js');
worker.postMessage({ n: 42 });
worker.onmessage = (e) => console.log(e.data);
worker.onerror = (e) => console.error(e);
// worker.js:
self.onmessage = (e) => {
const result = heavyCompute(e.data.n);
self.postMessage(result);
};
// Node:
// import { Worker } from 'node:worker_threads';
// 大きなオブジェクトは transfer:
// postMessage(buf, [buf.buffer])

이벤트 루프

동기 코드가 먼저 실행되고, 마이크로태스크(Promise)가 매크로태스크(setTimeout)보다 먼저다. 블로킹은 모든 것을 멈추게 한다.

1
2
3
4
5
6
7
8
9
console.log('1'); // 同期
Promise.resolve().then(() =>
console.log('2')); // マイクロタスク
setTimeout(() => console.log('3'), 0); // マクロタスク
console.log('4');
// 出力順:1 4 2 3
// 長時間タスクがイベントループをブロック:
// for (let i=0;i<1e9;i++){} // 固まる
// 分割または譲歩:await new Promise(r => setTimeout(r, 0))

제너레이터

function* 제너레이터는 지연 생성한다. yield로 일시정지/재개. Generator 타입 표기.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function* count(max: number): Generator<number> {
let i = 0;
while (i < max) {
yield i++; // 一時停止して値を返す
}
}
for (const n of count(3)) console.log(n); // 0 1 2
// 手動で進める:
const g = count(2);
g.next(); // { value: 0, done: false }
g.next();
// 無限シーケンス + 遅延評価:
function* naturals(): Generator<number> {
let n = 0;
while (true) yield n++;
}
// イテレータプロトコルと等価

비동기 이터레이터

for await로 비동기 데이터 소스를 순회한다. 비동기 제너레이터는 async function*. 스트림 데이터를 소비한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function* generate(): AsyncGenerator<number> {
let i = 0;
while (i < 3) {
await new Promise(r => setTimeout(r, 100));
yield i++;
}
}
// 消費:
for await (const n of generate()) {
console.log(n); // 0 1 2
}
// 非同期イテラブルの利用例:
const res = await fetch('/big');
const reader = res.body!.getReader();
// Node ストリーム:
// for await (const chunk of createReadStream('f'))
// 遅延 + バックプレッシャー、メモリに優しい

동시성 제한

동시에 실행되는 비동기 작업 수를 제어한다. 배치, 세마포어 또는 p-limit 방식. 자원 과부하를 피한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function mapLimit<T, R>(
items: T[], limit: number, fn: (x: T) => Promise<R>,
): Promise<R[]> {
const results: R[] = [];
let i = 0;
const workers = Array.from({ length: Math.min(limit, items.length) },
async () => {
while (i < items.length) {
const idx = i++;
results[idx] = await fn(items[idx]);
}
});
await Promise.all(workers);
return results;
}
// 用途:分割ダウンロード/リクエスト、limit で並列度を制御
// 一括送信を避けたいバッチ処理など

15.네트워크와 모듈

모듈 시스템, import/export, fetch와 타입이 지정된 API 캡슐화.

ES 모듈

import/export 정적 가져오기/내보내기. TS 타입 내보내기는 export type을 쓴다. 모듈은 스코프를 격리한다.

1
2
3
4
5
6
7
8
9
10
11
// utils.ts:
export const version = '1.0';
export function add(a: number, b: number): number { return a + b; }
export type ID = string; // 型のエクスポート
export default class App { } // デフォルトエクスポート
// main.ts:
import App, { add, version, type ID } from './utils';
// リネーム:
import { add as plus } from './utils';
// 全体インポート:
import * as utils from './utils';

타입 가져오기

import type은 타입만 가져오며 컴파일 시 지워진다. 런타임 의존과 순환 참조를 피한다.

1
2
3
4
5
6
7
8
9
10
11
// BAD:型がランタイムに残る(esbuild で残ることがある)
// GOOD:
import type { User } from './types';
// ミックス:
import { fetchUsers, type User } from './api';
// 型のみ:
import type { Options } from './config';
// 型のエクスポート:
export type { ID } from './utils';
// コンパイラが自動で elide、明示するとより明確
// 循環参照時は import type で断ち切る

타입이 지정된 API 캡슐화

fetch를 캡슐화해 강한 타입을 반환한다. 응답 검증, 오류를 통일해 처리. 제네릭 요청 함수.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function api<T>(
url: string, opts?: RequestInit
): Promise<T> {
const res = await fetch(url, opts);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return res.json() as Promise<T>;
}
// 使用例:
interface User { name: string }
const user = await api<User>('/api/user');
// ジェネリクスで呼び出し側が型安全に
// 境界では schema バリデーション(zod)で実行時のズレを防ぐ
// import { z } from 'zod';

Node HTTP 서비스

node:http 또는 프레임워크(Express/Fastify). 타입화된 요청 응답. 라우팅 처리.

1
2
3
4
5
6
7
8
9
10
11
import { createServer } from 'node:http';
const server = createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
});
server.listen(3000);
// Express:
// import express from 'express';
// @types/express が型を提供
// パラメータの型:
// app.get('/user/:id', (req: Request<{id: string}>, res: Response) => {})

DOM 타입

document.getElementById 반환 타입, 이벤트 타입, HTML 요소 타입 매핑.

1
2
3
4
5
6
7
8
9
10
const btn = document.getElementById('btn');
// HTMLElement | null、非 null チェックが必要:
if (btn) { btn.addEventListener('click', handler); }
// 具体要素にアサート:
const input = document.querySelector('input') as HTMLInputElement;
// イベント型:
function handler(e: MouseEvent) { console.log(e.clientX); }
// ジェネリックイベント:
const f = (e: KeyboardEvent) => { e.key };
// フォームの値取得:input.value は string

URL과 매개변수

URLSearchParams로 쿼리 문자열 구성, URL 파싱. 타입 안전 매개변수 읽기.

1
2
3
4
5
6
7
8
9
const params = new URLSearchParams({ q: 'ts', page: '2' });
params.toString(); // 'q=ts&page=2'
const url = new URL('https://example.com/search?q=ts');
const q = url.searchParams.get('q'); // 'ts'
// 組み立て:
url.searchParams.set('page', '3');
// リクエストヘッダ:
headers.append('Authorization', `Bearer ${token}`);
// パラメータ型:get は string | null を返すため絞り込んで使う

WebSocket

WebSocket 양방향 통신. onmessage 이벤트, 제네릭 데이터, readyState 상태.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const ws = new WebSocket('wss://example.com/socket');
// 接続確立:
ws.onopen = () => ws.send(JSON.stringify({ type: 'join' }));
// 受信:
ws.onmessage = (e: MessageEvent) => {
// e.data は string | Blob | ArrayBuffer の可能性
const data = JSON.parse(e.data as string);
console.log(data);
};
ws.onerror = (e) => console.error('连接错误', e);
ws.onclose = (e) => console.log('关闭', e.code);
// 能動的に閉じる:
ws.close(1000, '正常关闭');
// 自動再接続:onclose 内で setTimeout 再接続

요청 헤더와 인증

Headers 타입 안전 설정. Authorization Bearer, Content-Type. 인터셉터로 일괄 주입.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.set('Authorization', `Bearer ${token}`);
const res = await fetch('/api', { headers });
// レスポンスヘッダ読み取り:
const type = res.headers.get('content-type');
// 共通化ラッパー:
function authFetch(url: string, init?: RequestInit) {
return fetch(url, {
...init,
headers: {
...init?.headers,
Authorization: `Bearer ${getToken()}`,
},
});
}
// 注意:token 漏洩防止のためログ/URL に含めない
// リフレッシュ:401 時に token を更新して再試行

16.시간과 날짜

Date 객체, 타임스탬프, 서식 지정과 시간대.

Date 기초

Date 생성, getFullYear/getMonth/getDate 읽기, set 계열 설정. 월은 0부터 시작.

1
2
3
4
5
6
7
8
9
10
11
const now = new Date();
const d = new Date('2026-08-02T12:00:00Z');
const y = d.getFullYear(); // 2026
const m = d.getMonth(); // 7(0 から!)
const day = d.getDate(); // 2
// 設定:
d.setFullYear(2030);
d.setMonth(0); // 1 月
// ローカルと UTC:
// getUTCFullYear() / getUTCHours()
// getTimezoneOffset() 分差

타임스탬프

getTime() 밀리초 타임스탬프, Date.now(), Date.parse. 날짜 비교는 타임스탬프 사용.

1
2
3
4
5
6
7
8
9
const ms = Date.now(); // 現在のミリ秒
const d = new Date(ms);
const older = new Date('2020-01-01');
if (d.getTime() > older.getTime()) { } // 比較
// 秒単位:Math.floor(Date.now() / 1000)
// Date.parse('2026-08-02') はミリ秒を返す
// 日時の加算:
d.setDate(d.getDate() + 7); // 7 日加算
// setMonth/setDate は月跨ぎを自動処理

서식 지정

toISOString UTC 형식, toLocaleDateString 로컬 형식, Intl.DateTimeFormat 사용자 정의.

1
2
3
4
5
6
7
8
9
10
const d = new Date();
const iso = d.toISOString(); // '2026-08-02T04:00:00.000Z'
const local = d.toLocaleDateString('zh-CN');
// Intl でカスタム:
new Intl.DateTimeFormat('zh-CN', {
year: 'numeric', month: 'long', day: 'numeric',
hour: '2-digit', minute: '2-digit',
}).format(d);
// 相対時間:
// d.toLocaleTimeString('zh-CN')

시간대

타임스탬프는 UTC 밀리초이며, 서식 지정 시에만 로컬 시간대가 적용됨. Intl은 timeZone 옵션으로 시간대를 지정함.

1
2
3
4
5
6
7
8
9
10
11
const d = new Date();
// タイムゾーン指定:
new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
hour12: false,
}).format(d);
// getTimezoneOffset はローカルと UTC の分差を返す
// 保存は ISO/タイムスタンプ、表示はローカライズ
// タイムゾーン跨ぎ変換:
// UTC で保存、読み出し時に toLocaleString('zh-CN', { timeZone })
// 単純計算は dayjs/date-fns ライブラリ

타이머

setTimeout 지연, setInterval 주기, clearTimeout 취소. 반환 타입은 number.

1
2
3
4
5
6
7
8
9
10
11
const timer = setTimeout(() => {
console.log('1s 后');
}, 1000);
clearTimeout(timer); // キャンセル
const interval = setInterval(() => {
console.log('每秒');
}, 1000);
clearInterval(interval); // 停止
// 非同期待機:
await new Promise(r => setTimeout(r, 500));
// 注意:タイマーコールバックはイベントループのマクロタスクで実行される

기간과 구간

두 타임스탬프의 차는 밀리초 기간. 구간 판단은 타임스탬프 비교 사용. setInterval 드리프트 남용 방지.

1
2
3
4
5
6
7
8
9
10
11
const start = Date.now();
// 経過時間計測:
const elapsed = Date.now() - start; // ミリ秒
console.log(`${elapsed}ms`);
// 区間判定:
const inWindow = t >= start && t <= end;
// N ミリ秒ごと(ポーリング):
const poll = setInterval(() => { }, 1000);
// setInterval のドリフト補正:
// setTimeout 再帰 + 補正計算
// 精度:performance.now() の方が高い

날짜 라이브러리

dayjs/date-fns는 명확한 API와 시간대 처리를 제공함. 용량이 작고 불변임. 복잡한 시간대는 라이브러리 사용 권장.

1
2
3
4
5
6
7
8
9
10
11
12
13
// dayjs:
// import dayjs from 'dayjs';
// dayjs().format('YYYY-MM-DD');
// dayjs().add(7, 'day').toDate();
// dayjs('2026-08-02').isBefore('2026-09-01');
// date-fns:
// import { format, addDays, isBefore } from 'date-fns';
// format(new Date(), 'yyyy-MM-dd');
// addDays(new Date(), 7);
// タイムゾーン:
// import { formatInTimeZone } from 'date-fns-tz';
// formatInTimeZone(d, 'Asia/Shanghai', 'yyyy-MM-dd HH:mm')
// いずれもイミュータブル:新しい値を返し元の Date は変更しない

고정밀 타이밍

performance.now()는 시스템 시간 조정의 영향을 받지 않는 밀리초 단위 고정밀 시간. 성능 측정용.

1
2
3
4
5
6
7
8
9
10
11
12
13
const t0 = performance.now();
// コードを計測…
const elapsed = performance.now() - t0;
console.log(`${elapsed.toFixed(2)}ms`);
// 壁時計時間:Date.now() は変更され得る
// performance.now() は単調増加
// ブラウザ/Node 両方で利用可能
// マーカー分析:
performance.mark('start');
// …
performance.mark('end');
performance.measure('任务', 'start', 'end');
// 計測結果:performance.getEntriesByName('タスク')

17.프로세스와 시스템

Node 프로세스, 명령행 도구, 표준 스트림과 빌드 산출물 실행.

Node 프로세스

process 전역 객체: argv 매개변수, env 환경, exit 종료 코드, stdout/stderr 스트림.

1
2
3
4
5
6
7
8
9
10
import { argv, env, exit, stdout, stderr } from 'node:process';
const args = argv.slice(2);
const mode = env.NODE_ENV;
exit(0); // 正常終了
stdout.write('输出');
stderr.write('错误');
// 終了コード:0 成功、1 一般エラー
process.exitCode = 1; // 優雅に設定
// シグナル処理:
process.on('SIGINT', () => { console.log('Ctrl+C'); process.exit(0); });

명령행 도구

CLI 작성: 매개변수 파싱, 도움말 출력, 종료 코드. shebang으로 스크립트 실행 가능하게 함.

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env node
const [cmd, ...rest] = process.argv.slice(2);
if (cmd === '--help' || cmd === '-h') {
console.log('用法: ts-tool <命令> [选项]');
process.exit(0);
}
if (!cmd) {
console.error('缺少命令');
process.exit(1);
}
// 引数パースライブラリ:commander / yargs
// オプション:
// ts-tool build --out dist --watch

표준 스트림

stdin으로 입력 읽기, stdout으로 출력, stderr로 오류. readline 상호작용. 파이프 데이터.

1
2
3
4
5
6
7
8
9
10
11
import { stdin, stdout } from 'node:process';
import * as readline from 'node:readline/promises';
const rl = readline.createInterface({ input: stdin, output: stdout });
const name = await rl.question('名字? ');
console.log(`你好, ${name}`);
rl.close();
// 全 stdin を読み込み:
import { readFileSync } from 'node:fs';
// パイプ:echo hi | ts-tool
// 行ごと処理:
for await (const line of rl) { process(line); }

컴파일 산출물 실행

tsc 컴파일 후 node dist/main.js로 실행. package.json bin으로 명령 등록. 타입 선언 d.ts.

1
2
3
4
5
6
7
8
9
10
11
// package.json:
// {
// "bin": { "ts-tool": "./dist/cli.js" },
// "types": "./dist/index.d.ts",
// "main": "./dist/index.js"
// }
// コンパイル:npx tsc
// 実行:node dist/cli.js
// npm 公開:npm publish
// 宣言ファイル .d.ts を他 TS プロジェクトが参照可能
// tsconfig declaration: true

시스템 명령 실행

execFile로 외부 명령 실행, spawn은 스트리밍. child_process 타입. 인젝션 방지를 위한 이스케이프 주의.

1
2
3
4
5
6
7
8
9
10
11
import { execFile } from 'node:child_process';
execFile('ls', ['-l'], (err, stdout) => {
if (err) { console.error(err); return; }
console.log(stdout);
});
// ストリーミング spawn:
import { spawn } from 'node:child_process';
const child = spawn('node', ['worker.js']);
child.stdout.on('data', (d) => console.log(d.toString()));
// セキュリティ:exec + 文字列連結は避ける
// 引数は配列で渡し、シェルコマンドを連結しない

종료 코드

0은 성공, 0이 아니면 실패. 관례: 1은 일반 오류, 2는 사용법 오류. 스크립트는 CI에서 종료 코드에 의존함.

1
2
3
4
5
6
7
8
9
process.exit(0); // 成功
process.exit(1); // 一般エラー
process.exit(2); // 使用法/引数エラー
// 未捕捉例外は終了コード 1
// 手動設定:
process.exitCode = 2;
// CI で終了コードが 0 以外は失敗:
// ts-tool check && echo OK || echo FAIL
// シグナル終了:SIGTERM はデフォルト 143

npm scripts

package.json scripts로 명령 오케스트레이션. pre/post 훅, && 직렬, & 병렬. 빌드 체인에서 자주 사용.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// package.json:
// {
// "scripts": {
// "dev": "tsx src/dev.ts",
// "typecheck": "tsc --noEmit",
// "lint": "eslint src",
// "test": "vitest run",
// "build": "npm run typecheck && tsup src/index.ts",
// "prepublishOnly": "npm run build"
// }
// }
// 連結:&&(失敗で中断)
// 並列:& または concurrently ライブラリ
// pre/post フック:prebuild は build 前に自動実行
// npx で一時実行:npx vitest

설정과 환경

dotenv로 .env 로드, 타입화된 설정 객체, 런타임 검증. 설정과 코드 분리.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// インストール:npm i dotenv
// import 'dotenv/config';
const port = Number(process.env.PORT ?? 3000);
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
throw new Error('缺少 DATABASE_URL');
}
// 型付き設定:
interface Config {
port: number;
debug: boolean;
apiKey: string;
}
// バリデーション関数が Config を返し、型を保証
// .env はバージョン管理外(gitignore)
// 環境差分:.env.development / .env.production

18.정규식과 텍스트 처리

정규식 문법, 플래그, 타입화된 매칭과 텍스트 처리 관용구.

정규식 문법

리터럴 /.../ 또는 RegExp 생성. 문자 클래스, 수량자, 그룹, 앵커.

1
2
3
4
5
6
7
8
9
const re = /\b\w+@\w+\.com\b/;
// \d 数字 \w 単語 \s 空白 \b 単語境界
// [abc] 文字クラス [^abc] 否定
// * 0 回以上 + 1 回以上 ? 0 または 1 {2,4} 区間
// ^ 先頭 $ 末尾
// () グループ (?:) 非キャプチャ
// または:/cat|dog/
// テスト:
re.test('hi [email protected]'); // true

플래그

g 전역, i 대소문자 무시, m 다중 행, s 점이 줄바꿈 매칭, u 유니코드, y 고정.

1
2
3
4
5
6
7
8
9
10
const g = /a/g; // グローバル(matchAll に必要)
const i = /HELLO/i; // 大文字小文字無視
const m = /^line/m; // 各行のアンカー
const s = /a.b/s; // . が改行にもマッチ
const u = /\p{Emoji}/u; // unicode プロパティ
const y = /a/y; // スティッキー(lastIndex から)
// 組み合わせ:/foo/gim
// 動的構築:
new RegExp(`\\d{${len}}`, 'gi');
// リテラルで \ はエスケープ必須

매칭과 추출

match는 배열 반환(0 전체 매칭 + 캡처 그룹), matchAll은 전역 순회, match 실패 시 null이므로 null 확인 필요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const s = 'id=123&id=456';
const re = /id=(\d+)/g;
// 全件マッチ:
for (const m of s.matchAll(re)) {
console.log(m[1]); // '123' '456'
}
// 単発:
const first = s.match(re);
if (first) { console.log(first[0]); }
// 名前付きキャプチャ:
const named = /(?<year>\d{4})-(?<month>\d{2})/;
const r = s.match(named);
if (r) { r.groups!.year; }
// 最小マッチ ?:/(\d+?)(x)/ で最少マッチ

치환

replace는 문자열/함수 치환. $1 캡처 참조, g 플래그로 전체 치환. 함수형 치환으로 로직 처리.

1
2
3
4
5
6
7
8
9
10
11
const s = '2026-08-02';
// キャプチャ参照:
const a = s.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');
// 関数で置換:
const b = s.replace(/(\d+)/g, (m) => String(Number(m) + 1));
// 全置換(g 必須):
'aaa'.replace(/a/g, 'b'); // 'bbb'
// 空白除去:
s.replace(/\s+/g, ' ').trim();
// 単純置換は split/join も可:
s.split('-').join('/');

검증 관용구

전체 매칭은 ^...$ 앵커 사용. 숫자/이메일/URL의 일반적인 패턴. test는 boolean 반환.

1
2
3
4
5
6
7
8
9
10
11
12
13
function isEmail(v: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
}
function isInt(v: string): boolean {
return /^[-+]?\d+$/.test(v);
}
function isUrl(v: string): boolean {
try { new URL(v); return true; }
catch { return false; }
}
// 厳密一致は ^ で始まり $ で終わる必須
// 長さチェック:/^.{8,20}$/
// 貪欲を回避:/^\.$/ でドットをマッチ

정규식 성능

재앙적 백트래킹 방지: 중첩 수량자. 정규식 사전 컴파일. g 플래그의 lastIndex 상태 주의.

1
2
3
4
5
6
7
8
9
10
11
// BAD:壊滅的バックトラック
// /^(a+)+$/ が 'aaaaaaaaaaaaaaaaaaaa!' にマッチするのは極めて遅い
// GOOD:ネストした量詞を避ける
/^(a+)$/;
// 事前コンパイルで再生成を避ける:
const re = /\d+/g; // モジュールレベルで再利用
// g フラグは状態を持つ:
re.lastIndex = 0; // リセット
// 大きなテキストはチャンク処理
// 単純パースは indexOf / split を優先
// 正規表現は構造マッチのみ、ビジネスロジックはコードで

정규식 일반적인 함정

리터럴 이스케이프, g 플래그의 lastIndex 상태, 탐욕적 매칭, 문자열에서 \의 이중 이스케이프.

1
2
3
4
5
6
7
8
9
10
11
12
13
// 文字列構築時 \ は 2 回書く:
new RegExp('\\d+'); // /\d+/ と同等
// g フラグは lastIndex 状態を持つ:
const re = /a/g;
re.lastIndex = 0; // 使用前にリセット
// 貪欲マッチ:
'<a><b>'.match(/<.*>/); // 最後の > までマッチ
'<a><b>'.match(/<.*?>/); // 非貪欲、最短
// リテラル . はエスケープ:
/1\.0/; // '1.0' にマッチ、'1X0' ではない
// [] 内の ^ は否定:
/[^a]/; // a 以外
// 空マッチ:/(?:)/ は任意の位置にマッチ

일반적인 패턴

신분증/휴대폰 번호/색상/날짜 등의 일반적인 정규식 조각. 비즈니스 형식 검증에 매칭.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 携帯番号(中国大陸):
const mobile = /^1[3-9]\d{9}$/;
// メール:
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// 16 進数カラー:
const color = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
// 日付 yyyy-mm-dd:
const date = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// URL:
const url = /^https?:\/\/[^\s]+$/;
// 中文字符:
const zh = /[\u4e00-\u9fff]/;
// 空行:
const blank = /^\s*$/;
// 記憶要点:^$ アンカー、数量限定、文字クラス

19.빌드와 엔지니어링

tsconfig, 번들링 도구, Lint/포맷, 테스트와 CI.

tsconfig 상세

일반적인 컴파일 옵션: moduleResolution, declaration, noUnusedLocals, esModuleInterop, paths 경로 별칭.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true, // .d.ts を生成
"outDir": "dist",
"noUnusedLocals": true,
"paths": { "@/*": ["./src/*"] },
"resolveJsonModule": true
},
"include": ["src"]
}
// npm-run: tsc --noEmit で型チェック

번들링 도구

Vite/Webpack/Rollup 번들링. Vite는 TS/프론트엔드 기본. 라이브러리는 tsup/Rollup으로 ESM+CJS 출력.

1
2
3
4
5
6
7
8
9
10
11
12
// Vite:dev サーバー + ビルド
// vite.config.ts:
export default {
build: { target: 'esnext' },
// plugins: [react(), vue()]
};
// コマンド:
// npm run dev 開発
// npm run build ビルド
// ライブラリビルド:tsup
// tsup src/index.ts --format esm,cjs --dts
// 環境変数:import.meta.env.VITE_XXX

Lint와 포맷

ESLint 규칙 검사, Prettier 포맷. ts-eslint은 타입 인식 규칙 제공.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ESLint 設定:
// {
// "parser": "@typescript-eslint/parser",
// "plugins": ["@typescript-eslint"],
// "rules": {
// "@typescript-eslint/no-explicit-any": "warn"
// }
// }
// コマンド:
// npx eslint src --fix
// Prettier:
// npx prettier --write "src/**/*.ts"
// ルールテンプレート:
// npx eslint --init
// コミット前:husky + lint-staged

테스트

Vitest/Jest 단위 테스트. describe/it/expect. 타입과 실행 분리. ts 직접 테스트.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { describe, it, expect } from 'vitest';
import { add } from './add';
describe('add', () => {
it('两数相加', () => {
expect(add(1, 2)).toBe(3);
});
it('类型错误编译失败', () => {
// add('a', 2) // コンパイル時にブロック
});
});
// 型テスト:
// import { expectType } from 'tsd';
// カバレッジ:vitest run --coverage
// Mock:vi.mock() / vi.fn()

CI와 배포

CI 단계: 타입 검사, Lint, 테스트, 빌드. tsc --noEmit으로 타입 오류 차단.

1
2
3
4
5
6
7
8
9
10
11
// GitHub Actions:
// steps:
// - run: npm ci
// - run: npx tsc --noEmit # 型チェック
// - run: npx eslint src
// - run: npm test
// - run: npm run build
// - run: npm publish --dry-run
// 型チェックを最初に:失敗ですぐに中断
// キャッシュ:actions/cache で依存を高速化
// マルチバージョン:node 18/20/22 マトリクス

디버깅

sourceMap + Node inspect 중단점 디버깅. console 디버깅, 타입 단언 보조.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// tsconfig sourceMap: true
// VS Code launch.json:
// {
// "type": "node",
// "request": "launch",
// "program": "${file}",
// "runtimeArgs": ["--loader", "tsx"]
// }
// コマンド:
// node --inspect dist/main.js
// ブラウザ:DevTools Sources + sourcemap
// 型のデバッグ:
// type Debug<T> = T; hover で確認
// console 出力で位置特定をサポート

npm 배포

라이브러리 배포: files로 배포 내용 지정, version 시맨틱, main/types/exports 진입점. 배포 전 빌드.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// package.json:
// {
// "name": "@org/my-lib",
// "version": "1.2.0",
// "main": "./dist/index.js",
// "types": "./dist/index.d.ts",
// "exports": {
// ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs" }
// },
// "files": ["dist"],
// "sideEffects": false
// }
// 公開:
// npm run build && npm publish
// ドライラン:npm publish --dry-run
// バージョン:npm version patch|minor|major
// 権限:npm publish --access public

monorepo 설정

npm/pnpm workspaces 다중 패키지 저장소. 공유 의존성, 패키지 간 참조, 통합 스크립트.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// pnpm-workspace.yaml:
// packages:
// - 'packages/*'
// - 'apps/*'
// npm workspaces:
// {
// "workspaces": ["packages/*"]
// }
// パッケージ間参照:
// npm i @org/shared -w packages/web
// ルートスクリプトで一括:
// pnpm -r run build
// 共通 TS 設定:
// tsconfig.base.json を各パッケージが extends
// 依存ホイスト:pnpm はデフォルト隔離、明示宣言が必要

공식 링크

공식 문서와 리소스에 대한 직접 링크.

버전 2.1.1