type UserId = number;
type PostId = number;
const userId: UserId = 888;
const postId: PostId = 24;
const register = (userId: UserId, postId: PostId) => {...}
register(postId, userId) // 引数の順番をミスっているが、NoError!!
number
type UserId = number & { "UserId": never };
type PostId = number & { "PostId": never };
UserId
と PostId
は全く別物の型だということがわかるtype Branded<Type, Data extends string> = Type & { [key in Data]: never };
type UserId = Branded<number, "UserId">;
const makeUserId = (n: number) => n as UserId;
userId()
に通せばいい// 型の定義
type Branded<Type, Data extends string> = Type & { [key in Data]: never };
type UserId = Branded<number, "UserId">;
type PostId = Branded<number, "PostId">;
// id生成用関数を定義
export const makeUserId = (n: number) => n as UserId;
export const makePostId = (n: number) => n as PostId;
// idを生成
const userId = makeUserId(888);
const postId = makePostId(24);
const register = (userId: UserId, postId: PostId) => {};
register(postId, userId); // error!
// idを生成
const userId = makeUserId(888);
const postId = 24; // makePostIdをしていない
register(userId, postId); // error!
const userId = makeUserId(888) as PostId; // error!
const userId = makeUserId(888);
const a = userId + userId // number
type
属性をもたせ、そこにliteral typeを仕込む refdata PhantomType x a = PhantomType a
x
と a
の型引数を取るが、左辺のbodyでは x
が使われていない x
のような型のことを幽霊型と呼ぶdata PostId
data UserId
type UID = PhantomType UserId Int
type PID = PhantomType PostId Int
UID
も PID
も型のbodyは同じく PhantomType Int
である UID
と PID
は全く別物として扱うことができるtype Id<T extends string> = number;
type UserId = Id<"UserId">;
type PostId = Id<"PostId">;
const userId = 24 as UserId; // number。この時点でムリってる
const postId = 24 as PostId; // number
const register = (userId: UserId, postId: PostId) => {};
register(userId, postId); // 引数の順番をミスっているが、NoError!!
type
は newtype
ではなくただのエイリアス