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?

More than 1 year has passed since last update.

たった12行でユーザー定義型ガードを型安全に書けるライブラリを作った

Posted at

はじめに

ユーザー定義型ガード、使ってますか?こんなやつです

const isString = (value: unknown): value is string => typeof value === 'string'

こういうやつを型安全に書けるライブラリを12行で作成しました
以下はその実装です
コピペでもいいですし、 predicatory という名前で npm に公開してるのでそこからインストールすることもできます

const excludeSymbol = Symbol()

const factory =
  <I, O extends I>(
    extract: (value: I) => O | typeof excludeSymbol
  ): ((value: I) => value is O) =>
  (value: I): value is O =>
    extract(value) !== excludeSymbol

export const guard = Object.assign(factory, {
  exclude: excludeSymbol,
} as const)

次のように使うことができます

// これは `(value: unknown) => value is string` になる
const isString = guard(value => typeof value === 'string' ? value : guard.exclude)

ユーザー定義型ガードの問題点

なぜこんなものを作ろうと思ったかというと、ユーザー定義型ガードを自前で実装しようとすると問題があるからです
具体的には型安全ではありません

上に挙げた isString 関数(引数として受け取ったものが string 型かどうかを判定するユーザー定義型ガード)を例にすると、以下のように条件式を反転させてもコンパイラは怒ってくれません

- const isString = (value: unknown): value is string => typeof value === 'string'
+ const isString = (value: unknown): value is string => typeof value !== 'string'

なんなら typeof value === 'number' とかにしてもコンパイルは通ってしまいますし、安全とは言えません
こんなシチュエーションも guard 関数を使えば型安全に書くことができます

// これは `(value: unknown) => value is number` になる
const isString = guard(value => typeof value === 'number' ? value : guard.exclude)

また、引数の型が予めわかっている場合( Array.filter() など)にも対応しています

type Foo = {
  foo: any
}

type Bar = {
  bar: any
}

const arr: (Foo | Bar)[] = []

// `Foo[]` になる
const foos = arr.filter(guard(fooOrBar => 'foo' in fooOrBar ? fooOrBar : guard.exclude))

おわりに

スターをいただけると泣いて喜びます
https://github.com/myrear/predicatory

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?