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

【TypeScript】共用体型の拡張メソッドもどき

Posted at

簡潔で不変なデータ型にいくつもメソッドを生やしていると、なんだか負けた気分になる。データ型の定義だけしておいて、後から必要なメソッドを必要なだけ増やしたい。しかも、共用体型で上手いこと動いて欲しい。
そんな欲望を叶えるために次のような型関数PipeSpaceと、関数pipeSpaceを作った。

欲望のかたまり

PipeSpace.ts
export type PipeSpace<T extends object> = Readonly<T & {
    pipe: <U>(callback: (self: PipeSpace<T>) => U) => U
}>;

export function pipeSpace<T extends object>(object: T): PipeSpace<T> {
    const self: PipeSpace<T> = {
        ...object,
        pipe: callback => callback(self)
    };
    return self;
}

データ型の定義例

例えばOption型を定義する場合、次のように書く。まず、PipeSpaceで型の定義を囲み、pipeSpaceでオブジェクトの生成を囲めば完璧だ。
メソッドは一つも実装していない。だが焦る必要はない。コーヒでも飲んで、一息ついてから取り掛かろう。

Option.ts
import {PipeSpace, pipeSpace} from './PipeSpace'

export type Option<T> = PipeSpace<None | Some<T>>;

export type None = PipeSpace<{
    isNone: true
}>;
export const none: None = pipeSpace({
    isNone: true
});

export type Some<T> = PipeSpace<{
    isNone: false,
    value: T
}>;
export function some<T>(value: T): Some<T> {
    return pipeSpace({
        isNone: false,
        value
    });
}

拡張例

さて、Option型の値の中身を変換するmapと、中身を上手いこと取り出してくれるgetOrElseが欲しくなったとする。
欲しいなら所望のロジックを持った関数を素直に書けばいい。ただし、処理対象のOption型の値は単独で受け取る必要がある。そこだけは注意しよう。

OptionUtil.ts
import {Option, some} from './Option';

export function map<T, U>(transformer: (value: T) => U) {
    return (option: Option<T>): Option<U> =>
        option.isNone
            ? option
            : some(transformer(option.value));
}

export function getOrElse<T>(defaultValue: T) {
    return (option: Option<T>): T =>
        option.isNone
            ? defaultValue
            : option.value;
}

export function hint<T>(option: Option<T>): Option<T> {
    return option;
}

使用例

この実装は次のように使える。
え?メソッドじゃないって???そんな細かいことは気にするな。pipe()に目を瞑れば、見た目はほとんどメソッドじゃないか。十分だろ?

index.ts
import {some, none} from './Option';
import * as Option from './OptionUtil';

console.log(some(5)
    .pipe(Option.getOrElse(-1)));
// => 5

console.log(some(5)
    .pipe(Option.map(x => x * x))
    .pipe(Option.getOrElse(-1)));
// => 25

console.log(none
    .pipe(Option.getOrElse(-1)));
// => -1

console.log(Option.hint<number>(none)
    .pipe(Option.map(x => x * x))
    .pipe(Option.getOrElse(-1)));
// => -1

おわり

君の欲望は、どんな型(かたち)をしているんだい?

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