[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
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1 (5) | 2024.10.05 |
---|---|
๋์ปค(Docker)์ ์ฟ ๋ฒ๋คํฐ์ค(Kubernetes) ๊ธฐ๋ณธ ๊ฐ๋ (0) | 2024.09.17 |
[Flutter] ํ๋ฌํฐ ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ถ for macOS (2) | 2024.09.03 |
[Dart] ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋ฐ์์ ๋คํธ ํ์ต - Part 2 (1) | 2024.08.21 |
[Dart] ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋ฐ์์ ๋คํธ ํ์ต - Part 1 (0) | 2024.08.21 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1
[Flutter] ํ๋ฌํฐ ๊ธฐ์ด ๋ด์ฉ ์ ๋ฆฌ - Part 1
2024.10.05 -
๋์ปค(Docker)์ ์ฟ ๋ฒ๋คํฐ์ค(Kubernetes) ๊ธฐ๋ณธ ๊ฐ๋
๋์ปค(Docker)์ ์ฟ ๋ฒ๋คํฐ์ค(Kubernetes) ๊ธฐ๋ณธ ๊ฐ๋
2024.09.17 -
[Flutter] ํ๋ฌํฐ ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ถ for macOS
[Flutter] ํ๋ฌํฐ ๊ฐ๋ฐ ํ๊ฒฝ ๊ตฌ์ถ for macOS
2024.09.03 -
[Dart] ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋ฐ์์ ๋คํธ ํ์ต - Part 2
[Dart] ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋ฐ์์ ๋คํธ ํ์ต - Part 2
2024.08.21