Help us understand the problem. What is going on with this article?

TypeScript 2.0のneverでTagged union typesの絞込を漏れ無くチェックする

More than 3 years have passed since last update.

はじめに

TypeScript 2.0で追加されるTagged (or discriminated) union typesにより、String literal typesのプロパティでif文やswitch文でObjectの型を絞り込むことができるが、同じく2.0で追加されるThe never typeと合わせて使うと、絞り込み対象のUnion Typesを網羅的にif文やswitch文で処理しているかどうかをコンパイラでチェックすることができる。

先日投稿したRedux typed actions でReducerを型安全に書く (TypeScriptのバージョン別)で作成したサンプルアプリのRedux Reducerを例に説明する。

このReducerは、Union TypesであるActions.Actionsを、switch caseにてtypeプロパティで判別して処理している。
その際、switch caseにてActions.Actionsのすべてのtypeに対して漏れなく処理していることをコンパイラレベルで検出しよう、というのが今回の話。

// Union Types
export type Actions = IncrementAction | DecrementAction | SetCounterAction;

export interface IncrementAction extends Action {
    type: 'INCREMENT';
}
export function increment(): IncrementAction {
    return {
        type: 'INCREMENT'
    };
}

export interface DecrementAction extends Action {
    type: 'DECREMENT';
}
export function decrement(): DecrementAction {
    return {
        type: 'DECREMENT'
    };
}

export interface SetCounterAction extends Action {
    type: 'SET_COUNT';
    payload: {
        count: number;
    }
}
export function setCount(num: number): SetCounterAction {
    return {
        type: 'SET_COUNT',
        payload: {
            count: num
        }
    };
}

// Reducer
export const appStateReducer = (state: AppState = init(), action: Actions.Actions) => {
    switch (action.type) {

        case 'INCREMENT':
            return Object.assign({}, state, {
                count: state.count + 1
            });

        case 'DECREMENT':
            return Object.assign({}, state, {
                count: state.count + -1
            });

        case 'SET_COUNT':
            return Object.assign({}, state, {
                count: action.payload.count
            });
    }

    return state;
};

never型を使わない場合

case 'SET_COUNT':の時の処理をまるっと消してみる。もちろん、何もチェックは働かずコンパイラはエラーを検出することはできない。

no-never.png

never型を使った場合

次にnever型を使ってみた場合。使い方は、下記のようにnever型の変数にaction変数をアサインする処理を追加する。

// neverでチェックするようにしたReducer
export const appStateReducer = (state: AppState = init(), action: Actions.Actions) => {
    switch (action.type) {

        case 'INCREMENT':
            return Object.assign({}, state, {
                count: state.count + 1
            });

        case 'DECREMENT':
            return Object.assign({}, state, {
                count: state.count + -1
            });

        case 'SET_COUNT':
            return Object.assign({}, state, {
                count: action.payload.count
            });

        // never型でチェック
        default: 
            const _exhaustiveCheck: never = action;
    }

    return state;
};

これで先ほどと同じくcase 'SET_COUNT':の処理を消すと...

use-never.png

このように、never型にアサイン不可のためコンパイラによりエラーが検出される。
Actions.Actionsのすべてのtypeプロパティに対して処理をしている場合はここは到達不可能となり、actionnever型になる。よってnever型の変数へのアサインではエラーにならなくなるというわけだ。

if文でnever型を使った場合

念のためif文でも動作確認してみたところ、TypeScript 2.0では不足時のチェックは働くが、すべての条件を網羅的に書いた時もコンパイラエラーが出てしまうようで駄目でした。

if-never.png

一方、npm i typescript@nextでインストール可能な開発中のTypeScript 2.1だと上記の誤検知エラーはなくなり大丈夫でした。

まとめ

というわけではやく2.1がリリースされて欲しい...!!

参考

今回のnever型を使ったチェックはTypeScript Deep Dive紹介されていました

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした