[TS] νμ μ€ν¬λ¦½νΈ λΈλλλ νμ
Branded Type
νμ μ€ν¬λ¦½νΈλ₯Ό μ¬μ©νλ€ λ³΄λ©΄ μλ‘ λ€λ₯Έ λ κ°μ²΄μ νΉμ μμ±μ΄ λμΌν νμ μ κ°μ§ λκ° λ§λ€. μ΄λ‘ μΈν΄ νμ μμ€ν μμ μ€λ₯κ° λ°μνμ§ μλλΌλ λ Όλ¦¬μ μ€λ₯λ νμ μμ μ± λ¬Έμ λ₯Ό μΌκΈ°ν μ μλ€.
type User = {
id: string;
name: string;
};
type Post = {
id: string;
ownerId: string;
comments: Comments[];
};
type Comments = {
id: string;
timestamp: string;
body: string;
authorId: string;
};
μ μμμμ User.id
, Post.id
, Comments.id
λ λͺ¨λ string
νμ
μ΄λ€. 첫 λ²μ§Έ μΈμ postId
, λ λ²μ§Έ μΈμ authorId
λ₯Ό λ°λ ν¨μμ μΈμ μμλ₯Ό λ°λλ‘ μ λ¬ν΄λ, λ νμ
μ΄ λ¬Έμμ΄λ‘ λμΌνκΈ° λλ¬Έμ μ€λ₯λ₯Ό κ°μ§νμ§ λͺ»νλ€. νμ
μμ€ν
μμ μλ¬κ° λ°μνμ§ μμμ§λ§, λ°νμμμ μμκ° λ°λ id
κ° λλ¬Έμ μλͺ»λ μλ΅μ λ°λ λ¬Έμ κ° λ°μνλ€.
async function getCommentsForPost(postId: string, authorId: string) {
const response = await api.get(
`/author/${authorId}/posts/${postId}/comments`,
);
return response.data;
}
const comments = await getCommentsForPost(user.id, post.id); // νμ
μλ¬ λ°μ μν¨
μ΄λ° λ¬Έμ λ₯Ό λ°©μ§νκΈ° μν΄ λΈλλλ νμ (Branded Types)μ μ¬μ©ν μ μλ€. λΈλλλ νμ μ κΈ°μ‘΄ νμ μ κ³ μ ν μλ³μλ₯Ό λνλ΄λ νλ‘νΌν°(λΌλ²¨)λ₯Ό μΆκ°νμ¬ λ³΄λ€ λͺ ννκ³ κ΅¬μ²΄μ μΈ νμ μ μμ±νλ€.
type Brand<K, T> = K & { __brand: T };
type UserID = Brand<string, "UserId">;
type PostID = Brand<string, "PostId">;
const userId = "123" as UserID;
console.log(typeof userId); // string
console.log(userId.__brand); // undefined
μ μ½λμμ Brand<K, T>
λ K
λ₯Ό κΈ°λ³Έ νμ
μΌλ‘, T
λ₯Ό κ³ μ ν μλ³μλ‘ μ¬μ©νμ¬ νμ
μ νμ₯νκ³ μλ€. μλ₯Ό λ€μ΄ K
λ₯Ό string
μΌλ‘ μ§μ νλ©΄, string
νμ
μ __brand
λΌλ νΉμ νλ‘νΌν°κ° λΆμ μλ‘μ΄ νμ
μ΄ μμ±λλ€.
μ΄λ __brand
νλ‘νΌν°λ λ°νμμμ μ‘΄μ¬νμ§ μκ³ , νμ
μ€ν¬λ¦½νΈ μ»΄νμΌλ¬κ° μλ‘ λ€λ₯Έ λΈλλλ νμ
μ ꡬλΆν λ μ¬μ©νλ λ©νλ°μ΄ν°λ€. μ΄λ₯Ό ν΅ν΄ λμΌν μμ νμ
μΈ string
μ μ¬μ©νλλΌλ, μ»΄νμΌλ¬λ μλ‘ λ€λ₯Έ νμ
μΌλ‘ μΈμνμ¬ νμ
μμ μ±μ΄ κ°νλλ€.
type Brand<K, T> = K & { __brand: T };
type UserID = Brand<string, "UserId">;
type PostID = Brand<string, "PostId">;
type CommentID = Brand<string, "CommentId">;
type User = {
id: UserID;
name: string;
};
type Post = {
id: PostID;
ownerId: string;
comments: Comments[];
};
type Comments = {
id: CommentID;
timestamp: string;
body: string;
authorId: UserID;
};
async function getCommentsForPost(postId: PostID, authorId: UserID) {
// ...
}
const user = {} as User;
const post = {} as Post;
// β TS2345: Argument of type UserID is not assignable to parameter of type PostID
const comments = getCommentsForPost(user.id, post.id);
π‘ μ λ€λ¦μ΄ μμ νμ μΌ λλ κ΅μ°¨ νμ μ μ¬μ©νμ¬(& μ°μ°μ) νμ λ 벨μμ μΆκ°μ μΈ μμ±μ λΆμ¬ν μ μλ€. νμ μ ν λΉν λ κ΅μ°¨ νμ νΉμ±μ λ°λΌ λͺ¨λ νΉμ±μ ν¬ν¨ν ꡬ체μ μΈ νμ μΌλ‘ κ²°μ λλλ°(λ νμ μ λͺ¨λ λ§μ‘±νλ νλμ νμ μμ±), μ λ€λ¦μ μμ νμ μ λκ²Όλ€λ©΄ μΆκ°λ μμ±μ νμ μμ μ±μ μν μ©λλ‘λ§ μ¬μ©λκ³ , λ°νμμμ μμ νμ μΌλ‘ νκ°λλ€.
κ°μ λ Branded Type
μμμ μ¬μ©ν __brand
νλ‘νΌν°λ λ°νμμλ μ‘΄μ¬νμ§ μμ§λ§, μ½λ μμ± μ μλ μμ±μ νμλΌμ μ κ·Ό κ°λ₯ν΄ λ³΄μ΄λ λ¨μ μ΄ μλ€. μλμ²λΌ μ λν¬ μ¬λ³Όμ νμ©νλ©΄ μ€λ³΅ ν€ μ¬μ©μ λ°©μ§ν μ μκ³ , declare
ν€μλμ ν¨κ» μ¬μ©νμ¬ νμ
μμ€ν
μλ§ μ‘΄μ¬νλ __brand
μ¬λ³Ό νλ‘νΌν°λ₯Ό μ μν΄μ μ κ·Όμ μ°¨λ¨ν μ μλ€.
declare const __brand: unique symbol;
type Brand<B> = { [__brand]: B };
type Branded<T, B> = T & Brand<B>;
type UserID = Branded<string, "UserId">;
const userId = "123" as UserID;
console.log(typeof userId); // string
console.log(userId.__brand); // TS2339: Property __brand does not exist on type UserID
unique symbol
μ νμ
μμ€ν
μμ κ³ μ μ±μ λμ± κ°νκ² λ³΄μ₯νμ¬ μ¬λ³Ό κ°μ μΆ©λμ λ°©μ§νκΈ° μν κΈ°λ₯μ΄λ€. μλ°μ€ν¬λ¦½νΈμ Symbolμ κ³ μ ν κ°μ κ°μ§μ§λ§, unique symbol
μ μ¬μ©νλ©΄ νμ
μμ€μμμ κ³ μ μ±κΉμ§ μ격νκ² κ΄λ¦¬ν μ μλ€. unique symbol
μ const
μ μΈμ΄λ ν΄λμ€μ readonly static
μμ±μμλ§ μ¬μ©ν μ μλ€.
declare
ν€μλλ μ€μ ꡬν μμ΄ νμ
μ λ³΄λ§ μ μΈν λ μ¬μ©νλ€. μ£Όλ‘ μΈλΆ λΌμ΄λΈλ¬λ¦¬λ κΈ°μ‘΄ λͺ¨λμ νμ
μ μ μνμ¬ νμ
μ€ν¬λ¦½νΈ μ»΄νμΌλ¬μκ² νμ
μ 보λ₯Ό μλ €μ£Όλ μν μ νλ€. declare
λ‘ μ μΈλ λ΄μ©μ μ»΄νμΌ μ νμ
체ν¬μλ§ μ¬μ©νκ³ , μ΅μ’
μ μΌλ‘ μμ±λλ μλ°μ€ν¬λ¦½νΈ μ½λμλ ν¬ν¨λμ§ μλλ€.
Use Cases
μ¬μ©μ μ λ ₯ λ°μ΄ν°λ₯Ό λ°μ μ ν¨μ±μ κ²μ¬ν ν, λΈλλλ νμ μ λ°ννμ¬ μΌλ° λ¬Έμμ΄κ³Ό ꡬλΆνλ λ°©μμΌλ‘ νμ μμ μ±μ λ κ°νν μ μλ€.
type EmailAddress = Brand<string, "EmailAddress">;
function validEmail(email: string): EmailAddress {
// μ΄λ©μΌ μ ν¨μ± κ²μ¬ λ‘μ§
return email as EmailAddress;
}
λΈλλλ νμ
μ μ¬μ©νλ©΄ λ ννλ ₯ μλ μ½λλ₯Ό μμ±ν μ μλ€. μλμ²λΌ CarBrand
, EngineType
κ³Ό κ°μ ꡬ체μ μΈ νμ
μ λμ
ν¨μΌλ‘μ¨, κ° κ°λ€μ΄ μ΄λ€ μλ―Έλ₯Ό κ°μ§λμ§ μ ννκ² λͺ
μν μ μλ€.
type CarBrand = Brand<string, "CarBrand">;
type EngineType = Brand<string, "EngineType">;
type CarModel = Brand<string, "CarModel">;
type CarColor = Brand<string, "CarColor">;
function createCar(
carBrand: CarBrand,
carModel: CarModel,
engineType: EngineType,
color: CarColor,
): Car {
// ...
}
// λ¬Έμμ΄ λ¦¬ν°λ΄μ CarBrand, CarModel, EngineType, CarColor νμ
μ΄ μλλ―λ‘ μλ¬ λ°μ
const car = createCar("Toyota", "Corolla", "Diesel", "Red"); // ts2345 Error
λ νΌλ°μ€
- Using Branded Types in TypeScript
- Leveraging TypeScript branded types for stronger type checks - LogRocket Blog
κΈ μμ μ¬νμ λ Έμ νμ΄μ§μ κ°μ₯ λΉ λ₯΄κ² λ°μλ©λλ€. λ§ν¬λ₯Ό μ°Έκ³ ν΄ μ£ΌμΈμ