3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

null のブランド型は作れない

Posted at

null に想いを込めたコードベースでその想いが込められたものだけ区別するためにブランド型にできないか試したのですが、できないようです


まず、基本的なブランド型の使い方を Branded Type ベストプラクティス 検索 #TypeScript - Qiita で確認してみます

const userIdBrand = Symbol();
const postIdBrand = Symbol();
type UserId = string & { [userIdBrand]: unknown };
type PostId = string & { [postIdBrand]: unknown };

// 説明用にUserIdを作る
const myId = "uhyo" as UserId;

// これは型エラーになる
const postId: PostId = myId;

これを string ではなく null に適用してみます

const PoBrand = Symbol();
const GaBrand = Symbol();

type Po = null & { [PoBrand]: unknown };
type Ga = null & { [GaBrand]: unknown };

// Po と Ga の型は別のブランド型だがエラーなく代入できる
const po = null as Po;
const nullPo: Ga = po;

この理由は単純で null & {}never と判断されるためです。そのため PoGa の型はどちらも never となり相互に代入が可能になります

ではなぜこのように変換されてしまうのでしょうか?
その前になぜ string & {} が有効な記述なのかを考えてみます。これはシンプルに StringObject を継承しているからですね

const str = new String()

str instanceof String // true
str instanceof Object // true

const obj = new Object()

obj instanceof String // false
obj instanceof Object // true

そのため、プロトタイプ拡張が可能でその場合の方の表現が string & {} になるわけです

String.prototype.hoge = 'hoge'
console.log(''.hoge) // 'hoge' が出力される

// 上記のコードから実際に推論されるわけではないが、この定義と等しい
type HogeString = string & { hoge: 'hoge' }

これに対して nullObject を継承しておらず当然のことながらプロトタイプ拡張はできません

null instanceof Object // false

null.prototype.hoge = 'hoge' // エラーになる

ということで null & {} は発生することがあり得ず、そのためあり得ないを表現する never 型になる、というわけでした

学び

とりあえず、 null に想いを込めるのはやめましょう

おまけ

nullObject を継承していないんですが、 typeof で確認するとなぜか 'object' になるみたいです。面白いですね

3
0
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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?