• 作成:

Immutable.js 4.0.0-rc.9はFlowでもTypeScriptでもgetとsetの文字列を検査してエラーを出します

Immutable.jsのRecordについて情報を察知しました.

class extendでRecordを継承して独自の型を作るやつです.

生JavaScript版, 型安全も何もない.

const { Record } = require("immutable");

class Person extends Record({ name: "", birthday: null }) {
  get age() {
    return new Date().getFullYear() - this.birthday.getFullYear();
  }
  年齢偽装() {
    return this.set("birthday", new Date("1950-01-01"));
  }
}

const ncaq = new Person({ name: "ncaq", birthday: new Date("1996-01-25") });

console.log(ncaq);
console.log(ncaq.name);
console.log(ncaq.set("name", "エヌユル"));
console.log(ncaq.age);
console.log(ncaq.年齢偽装());

実行結果

Person { name: "ncaq", birthday: Thu Jan 25 1996 09:00:00 GMT+0900 (JST) }
ncaq
Person { name: "エヌユル", birthday: Thu Jan 25 1996 09:00:00 GMT+0900 (JST) }
22
Person { name: "ncaq", birthday: Sun Jan 01 1950 09:00:00 GMT+0900 (JST) }

Flow版

Flow版はプロパティアクセスが使えません. なのでgetメソッドを使ってアクセスすることになります. 文字列を間違えるとエラーが出ます. Flowを使うの初めてだったので, birthdayの初期値を設定しないでコンストラクト時に要求する方法がわかりませんでした.

// @flow

import { Record } from "immutable";

class Person extends Record({ name: "", birthday: new Date() }) {
  get age() {
    return new Date().getFullYear() - this.get("birthday").getFullYear();
  }
  年齢偽装() {
    return this.set("birthday", new Date("1950-01-01"));
  }
}

const ncaq = new Person({ name: "ncaq", birthday: new Date("1996-01-25") });

console.log(ncaq);
console.log(ncaq.name);
console.log(ncaq.set("name", "エヌユル"));
console.log(ncaq.age);
console.log(ncaq.年齢偽装());

TypeScript版 TypeScript版はプロパティアクセスが使えます. 勿論プロパティを間違えるとエラーです. getメソッドも使えますが, Flow版とインターフェイスが違い, notSetValue引数を指定しないとgetできません.

import {Record} from 'immutable';

class Person extends Record({ name: "", birthday: null }) {
    get age() {
        return new Date().getFullYear() - this.birthday.getFullYear();
    }
    年齢偽装() {
        return this.set("birthday", new Date("1950-01-01"));
    }
}

const ncaq = new Person({ name: "ncaq", birthday: new Date("1996-01-25") });

console.log(ncaq);
console.log(ncaq.name);
console.log(ncaq.set("name", "エヌユル"));
console.log(ncaq.age);
console.log(ncaq.年齢偽装());

Immutable.js 4ではFlowでもTypeScriptでも型エラーを出してくれることがわかりました.

デフォルト値を設定せずにnullを排除しようと思ったけど思考が間違っていました

この章は思考が間違っているので読まなくて良いです.

どちらでもbirthdaynullを許可しないで, 初期値を設定せず, コンストラクタで設定することを要求する方法はわかりませんでした. TypeScriptではclass Person extends Record<{name: string, birthday: Date}>({ name: "", birthday: null }) みたいに書けることはわかりましたが, "strictNullChecks": trueにしたら当然ですがbirthday: nullの表記に怒り出すんですよね. かと言ってbirthday: nullを削除したら型が不一致になって怒られますし. これは独自のコンストラクタを設置してそこで値を要求し, デフォルト値は適当なものを入れておくしか無いのでしょうか? 使われないデフォルト値を設置するのも気が引けるのですが… 誰か教えてください.

これはnullを許可したいという話ではなく, nullを許可しないで, なおかつユーザがコンストラクタに値を入力することで設定することを強制したいというものです. 例えば今回の例ではbirthdayはユーザが入力して意味のある値で, これをクラス側が適当に現在時刻を入れてしまってはヘンなことになります.

普通のclassでは

class Person {
    name: string;
    birthday: Date;
    constructor(name: string, birthday: Date) {
        this.name = name;
        this.birthday = birthday;
    }
    get age() {
        return new Date().getFullYear() - this.birthday.getFullYear();
    }
}

const ncaq = new Person("ncaq", new Date("1996-01-25") );
const errorNcaq = new Person("ncaq", null ); // strictNullChecksを有効にするとエラー

console.log(ncaq);
console.log(ncaq.name);
console.log(ncaq.age);

のように初期値を設定せずに非nullに出来るじゃないですか. これをImmutable.jsのRecordで実現したい.

removeやdeleteInを持つImmutable.jsでデフォルト値とnull排除を求めるのが間違っていると気がついた

よく考えたらremoveを備えるImmutable.jsのRecordでこれを求めるというのがそもそも要件的に間違っていました.

Immutable.jsのRecordremoveはフィールドを削除するわけですが, 削除したフィールドは何になるかというとデフォルト値に戻るわけですね. だからデフォルト値を設定せずにnullを排除するのは無理です.

求めるものを間違っていたようですね.