12
2

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 3 years have passed since last update.

io-tsの基本的な使い方

Posted at

io-tsとは

Typescriptで型が合っているかどうかを検査したいときに使用する。
例えば、APIのリクエストパラメータにisActiveがあり、isActiveにはtrueまたはfalseが入っていることを期待しているとする。
しかし、実際には以下のような値が飛んでくる可能性がある。

  • "true" → 文字列なのでダメ
  • 1 → 数値なのでダメ

これらのパターンを全てのリクエストパラメータに対して検査するのはとても大変である。
しかし、io-tsを使用すれば、こんな感じにスッキリ書くことができる。

import * as t from 'io-ts';

const ApiRequest = t.type({
  isActive: t.boolean
});

const validation = ApiRequest.decode(req.body);

公式ドキュメントはこちら
サンプルコードも用意したので、よかったらご覧ください。

準備

$ yarn add io-ts fp-ts

fp-tsio-tsと依存関係にあるので、一緒にインストールする。
これらの他に、一緒に入れると便利なものを紹介しておく。

  • io-ts-reporters
    • decodeしたときに出力される文字列をいい感じに整形してくれる
  • io-ts-types
    • NonEmptyStringなど、便利な型が定義されている(後ほど紹介)

基本的な流れ

まずは、io-tsをインポートする

import * as t from 'io-ts';

次に型を定義する

const Book = t.type({
  id: t.number,
  title: t.string,
  author: t.string,
});

t.numbert.string以外にも、t.booleant.nullなどがある。
詳しくはこちらを参照。

最後に型が合っているかどうかチェックする

const data = {
  id: 1,
  title: 'タイトル',
  author: '著者',
};

const validation = Book.decode(data); // 型をチェック
console.log(validation); // 結果を確認

基本的にはこれだけ。非常にシンプル。

応用編

t.stringなどの基本的な型だけでなく、例えば、メールアドレスの形式が正しいかどうかなど、もっと厳密に値をチェックしたい場合もあると思う。
ここでは個人的に利用頻度が高いと思った書き方をいくつか紹介する。

OR

name: t.union([ t.string, t.null ])

この場合、namestringでもnullでもOKということになる。

特定の文字列のみ

const GenderTypes = {
  male: 'male',
  female: 'female',
  other: 'other',
};

const User = t.type({
  id: t.number,
  name: t.string,
  gender: t.keyof(GenderTypes),
});

この場合、genderは、"male", "female", "other"のいずれかの文字列のみOKということになる。

NonEmptyString

import { NonEmptyString } from 'io-ts-types';

const User = t.type({
  id: t.number,
  name: NonEmptyString,
});

io-ts-typesをインストールすると使える。
t.stringだと、スペースやタブだけの場合でも通ってしまうので、それらを弾くことができるNonEmptyStringは結構便利。

0以上の整数

interface IPositiveNumber {
  readonly PositiveNumber: unique symbol
};
const PositiveNumber = t.brand(
  t.number,
  (n): n is t.Branded<number, IPositiveNumber> => 0 < n,
  'PositiveNumber'
);

const User = t.type({
  id: t.number,
  name: t.string,
  age: t.intersection([t.Int, PositiveNumber]),
});

t.numberだと、小数値やマイナス値でも通ってしまうが、この書き方ならageは正の整数しか受け付けない。

メールアドレス形式

const REGEX_EMAIL = /^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
interface IEmail {
  readonly Email: unique symbol;
};
const Email = t.brand(
  t.string,
  (input): input is t.Branded<string, IEmail> => REGEX_EMAIL.test(input),
  'Email'
);

const User = t.type({
  id: t.number,
  name: t.string,
  email: Email,
});

こちらは正規表現を利用した書き方。パスワードの形式チェックなんかにも使える。

注意すべきこと

例えば下のように、パラメータが不足している場合、チェックの段階でキチンと弾かれる。

const Book = t.type({
  id: t.number,
  title: t.string,
  author: t.string,
});

// authorが不足している
const data = {
  id: 1,
  title: 'タイトル',
}

const validation = Book.decode(data); // NG

ところが、下のようにパラメータが余分な場合は、チェックが通ってしまうので要注意

const Book = t.type({
  id: t.number,
  title: t.string,
  author: t.string,
});

// priceが余分
const data = {
  id: 1,
  title: 'タイトル',
  author: '著者',
  price: 500,
}

const validation = Book.decode(data); // OK
12
2
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
12
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?