結論
io-tsを使うと、TypeGuard関数1を手で書かなくて済む。
import * as T from 'io-ts'
`interface {}`の定義と自動導出されたUser-Defined Type Guardの使用
// interface
const objectX = T.type({
x: T.number,
y: T.string,
})
type ObjectX = T.TypeOf<typeof objectX>
/* Same as
interface ObjectX {
x: number
y: string
}
*/
const a: unknown = {
x: 42,
y: 'inu',
}
// ---------------------------------
// User-Defined Type Guardは自動的に導出される
// ---------------------------------
const isObjectX: (w: unknown) => w is ObjectX = objectX.is
if (isObjectX(a)) {
console.log('a is an ObjectX.')
console.log(a.x)
console.log()
}
// a is an ObjectX.
// 42
//
和型`foo | bar`
// union
const unionX = T.union([
T.number,
T.string,
])
type UnionX = T.TypeOf<typeof unionX>
const b: unknown = 'neko'
if (unionX.is(b)) {
console.log('b is a UnionX.')
console.log()
}
// b is a UnionX.
//
積型`foo & bar`の定義とpartialプロパティ`nya?: Mew`
// partialを持つinterface
const objectY = T.intersection([
T.type({x: T.number}),
T.partial({y: T.string}),
])
type ObjectY = T.TypeOf<typeof objectY>
const c: unknown = { x: 42 }
if (objectY.is(c)) {
console.log('c is an ObjectY.')
console.log(c.x)
console.log()
}
// c is an ObjectY.
// 42
//
おまけ: partialプロパティを定義するなら、`T.partial()`を使った方がいい
下記の用法の代わりに、上記の「積型foo & bar
の定義とpartialプロパティnya?: Mew
」で使用した方法を使わないと、y: undefined
を明示的に書く必要がでてしまう。
const objectYNotBetter = T.type({
x: T.number,
y: T.union([T.string, T.undefined]),
})
type ObjectYNotBetter = T.TypeOf<typeof objectYNotBetter>
if (!objectYNotBetter.is(c)) {
console.log('c is not an ObjectYNotBetter, because c.y is omitted.')
}
const d: unknown = {
x: 42,
y: undefined,
}
if (objectYNotBetter.is(d)) {
console.log('d is an ObjectYNotBetter.')
console.log('We must define .y with undefined for kinds of objectYNotBetter.')
console.log(d.y)
console.log()
}
// c is not an ObjectYNotBetter, because c.y is omitted.
// d is an ObjectYNotBetter.
// We must define .y with undefined for kinds of objectYNotBetter.
// undefined
//
if (objectY.is(d)) {
console.log('But, no way, d is an ObjectY.')
console.log(d.y)
console.log()
}
// But, no way, d is an ObjectY.
// undefined
//
概要
僕はもう疲れてしまいました。
あらわれるいくつもの型!! 無限にUser-Defined Type Guardを書かなければいけない!!!
オラオラオラオラオラオラオラオラオラオラオラオラオラオラオラオラオラオラオラオラァ!!!
interface InuNekoMaid {
maid: boolean
wan: Bow
nya?: Mew
// ...
}
// User-Defined Type Guard
function isInuNecoMaid(x: any): x is InuNekoMaid {
return (
typeof x.maid === 'boolean' &&
isBow(x.wan) &&
(x.nya === undefined || isMew(nya))
// && ...
)
}
// これを定義する作業が型の数だけ続く。
型の種類は可算無限個あると、古事記にも書かれています。
つまり最悪な場合、我々は限りなくUser-Defined Type Guardを書かなければいけない
それはいけませんね。
型に対するマナーがなっていません!
ありえない話!!!!🤬😡🤬🤬😡🤬
io-tsを使いましょう。
io-tsとは
io-tsとはType<A, O, I>
という、**コーデック(codec)**と呼ばれるものを提供する、TypeScriptのライブラリです。
Type<A, O, I>
は以下の形で、O, A, I
型の間の関数をまとめたクラスです。
decode関数
I -----------> (A | Errors)
encode関数
A ----------> O
|
is関数 |
v
(x is A)
そしてそのType<A, O, I>
は、関数T.type()
と、型関数T.TypeOf<>
を用いて、自動的に生成されます。
interface
・X | Y
・X & Y
などのような型への、decode
・encode
・is
関数が自動で生成される、ということです。
ここで本稿の結論が出ました。
そう、is
関数です!
// interface
const objectX = T.type({
x: T.number,
y: T.string,
})
type ObjectX = T.TypeOf<typeof objectX>
/* Same as
interface ObjectX {
x: number
y: string
}
*/
// これ!!
const isObjectX: (w: unknown) => w is ObjectX = objectX.is
まとめ
あとは結論のままに、各型を構築するだけです。
簡単!
最高!
やったー!!
おまけ: partialプロパティを定義するなら、T.partial()
を使った方がいい
io-tsで
interface ObjectY {
x: number
y?: string // partialプロパティ
}
を定義したいときは、以下のようにする
const objectY = T.intersection([
T.type({x: T.number}),
T.partial({y: T.string}),
])
type ObjectY = T.TypeOf<typeof objectY>
以下のようにしない
const objectYNotBetter = T.type({
x: T.number,
y: T.union([T.string, T.undefined]),
})
type ObjectYNotBetter = T.TypeOf<typeof objectYNotBetter>
前者は次のような値を表すが、
// Compile OK
const y1: ObjectY = {
x: 10,
}
const y2: ObjectY = {
x: 10,
y: 20,
}
const y3: ObjectY = {
x: 10,
y: undefined,
}
後者は次のような値を表さない。
(.y
を省略できない。)
// Compile error
const wrong: ObjectYNotBetter = { x: 10 }
おわり
まだTypeScriptのTypeGuard関数を手動で書いてるんですか!?
マナー違反ですよ!!!
むせかえるような怠惰のにおい!!!
ありえない話!!!!🤬🤬😡🤬😡🤬
TODO
- Generics型をio-tsで書く場合の解説
-
ここでの「TypeGuard関数」とはUser-Defined Type Guardのことを指します。以下、User-Defined Type Guardと呼称。 ↩