10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeScript でも Nominal Typing がしたい

Last updated at Posted at 2019-03-11

RawPassword と HashedPassword

例えば Web アプリを作っているなかで、一言「パスワード」と言っても生のパスワードもあればハッシュ化されたものもある。
名前とかに気をつけて実装していれば、そこを取り違える危険はほとんど無いとはいえど、安易に password とか命名しちゃって「あれ?どっちだっけ?」って迷う事は良くあると思う。

そういった間違いをなくすために type alias 機能を使って RawPasswordHashedPassword を作ってみようと思う。

type RawPassword = string;
type HashedPassword = string;
type Password = RawPassword | HashedPassword;

RawPasswordHashedPassword もどちらも string 型で、PasswordRawPasswordHashedPassword のどちらにもなれる。

これで生かハッシュ化されているかで迷うことはない!

けれども全ては string

結果、次のコンパイルが成功する。

const passwordX: RawPassword = '1qaz2wsx';
const passwordZ: HashedPassword = passwordX;

.....これはまずい。すこぶるまずい。
型が付いて混乱しなくなると思ったら、型が付いてなおさら混乱した。
関数とかで紛れ込まれたらパスワードを平文で保存するサービスができかねない。。。

Structural Typing な TypeScript

なぜこんな事が起きるかというと、TypeScript は Structural Typing という型システムを採用しているから。
他には Go とかも同じ型システムを採用している。

これは「構造を元に型が決まる」というもので、たとえ違う型として定義されていても、構造が同じであれば同じ型として認めるという面白い仕組み。逆に「型を元に構造が決まる」方式を Nominal Typing と言うらしい。こちらの場合は構造が同じでも型が違えば同じ型としては_認めない_。

今回の場合、どちらも構造としては同じ string がベースになっているので、同じ型として扱われていた。

TypeScript で Nominal Typing

皆同じ事を考えるみたいで、TypeScript で Nominal Typing を実現するテクニックはあるらしい。

TypeScript Deep Dive の Nominal Typing という記事を参考に、自分で型を定義してみた。
それがこちら

type NominalTyping<T extends string> = { type: T };

export type RawPassword = NominalTyping<'RawPassword'> & string;
export type HashedPassword = NominalTyping<'HashedPassword'> & string;
export type Password = RawPassword | HashedPassword;

元の記事では & string という交差型を使うテクニックは利用していなかったために、文字列への/からの変換で一手間かかっていたけど、交差型を使うことで簡単にキャストできるようになった。

const raw: RawPassword = 'rawpassword' as RawPassword;
const hashed: HashedPassword = 'hashedpassword' as HashedPassword;

const rawToString: string = raw;
const hashedToString: string = hashed;

const tryHashed: RawPassword = hashed; // Compile Error
const tryRaw: HashedPassword = raw; // Compile Error

そして記事を書いている途中でまたしても更に優れた記事にぶち当たってしまった。。。
Nominal typing techniques in TypeScript の4番目の内容が一番スッキリしてる。

Nominal Typing って言葉、前にも見かけた気がするけど今回始めてちゃんと覚えた。

10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?