Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
21
Help us understand the problem. What is going on with this article?
@wadahiro

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紹介されていました

21
Help us understand the problem. What is going on with this article?
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
wadahiro
nri
NRIは「コンサルティング」「金融 ITソリューション」「産業 ITソリューション」「IT 基盤サービス」の4事業でお客様のビジネスや快適な社会、暮らしを支えています。※各記事の内容は個人の見解であり、所属する組織の公式見解ではありません。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
21
Help us understand the problem. What is going on with this article?