【TypeScript】プリミティブな値を特別な型と定義する

 TypeScriptでは型を定義できます。これは大体Objectの絡んだプロパティを見る時が多く、そうでなくとも配列内部に何が入るか、のようなものがほとんどで、これが基本的な使い方です。

type UserType = {
    id: number
    name: string
    memberType: number
}
type UsersType = Array<UserType>

 しかし時折、プリミティブ値にもバリデーション的に型を用いたくなります。
JavaScript のデータ型とデータ構造 – JavaScript | MDN#プリミティブ値
 上の例では memberType:string が問題になります。単に number では数値ならば何でもOKになってしまいます。これを防ぎます。
 型とは違いますが、動きや構造が少しでも複雑ならば値を表現するクラスを作るのがわかりやすいです。ドメイン駆動設計におけるValueObjectの考え方です。

const MEMBER_TYPE_NORMAL = 1;
const MEMBER_TYPE_RED    = 4;
const MEMBER_TYPE_BLUE   = 5;
const MEMBER_TYPE_WHITE  = 7;
const MEMBER_TYPES =[
    MEMBER_TYPE_NORMAL,
    MEMBER_TYPE_RED,
    MEMBER_TYPE_BLUE,
    MEMBER_TYPE_WHITE,
];
class MemberType{
    readonly value: number

    constructor(value: number) {
        if (!MEMBER_TYPES.include(value)) {
            throw new Error(`${value} は不正な値です`)
        }
        this.value = value
    }

    toString(): string {
        return '' + this.value
    }
}

type UserType = {
    id: number
    name: string
    memberType: MemberType
}
type UsersType = Array<UserType>

 こうすると memberType に不正な値を入れることはできなくなります。こうした時に少々面倒なのはクラスのインスタンス化処理の重さです。短い間隔のポーリングであったり、大量のデータを一度に受け取って加工するなどするとクラスを扱う処理の重さは案外問題になります(なりました)。TypeScriptは”自分には型があると思い込んでいるJavaScript”とも呼ばれるものでコンパイル後には処理をいくらか飛ばします。このため速度が問題になっているなどの時にはValueObjectでなくTypeScriptの型で特別な型を表現します。

const MEMBER_TYPE_NORMAL = 1;
const MEMBER_TYPE_RED    = 4;
const MEMBER_TYPE_BLUE   = 5;
const MEMBER_TYPE_WHITE  = 7;
const MEMBER_TYPES:Array<number> =[
    MEMBER_TYPE_NORMAL,
    MEMBER_TYPE_RED,
    MEMBER_TYPE_BLUE,
    MEMBER_TYPE_WHITE,
];

// numberでありユニークな型MemberTypeを作る
// @see https://qiita.com/suin/items/ae9ed911ebab48c98835 公称型をTypeScriptで実現するための基礎
declare const memberTypeSymbol: unique symbol
type MemberType = number & { [memberTypeSymbol]: never }

// この関数で返すBooleanは MemberType であることを示すためのBooleanとTypeScriptに示す
const isMemberType = (value: number): value is MemberType => {
    // MemberTypeであるために必須の条件を記述
    if (!MEMBER_TYPES.includes(value)) {
        throw new Error(`${value} はMemberTypeとして不正な値です`)
    }
    return true
}


type UserType = {
    id: number
    name: string
    memberType: MemberType
}
type UsersType = Array<UserType>

 このようにするとisMemberType関数を通すだけで済むようになります。

const currentMemberType = MEMBER_TYPE_WHITE;
function hoge(){
    if(!isMemberType(currentMemberType)){
        return;
    }
    const user: UserType = {
        id: 1,
        name: '太郎',
        memberType: currentMemberType,
    }
}
>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG