型強化の呪文。noImplicitAny

www.infoq.com

上記記事をたまたま読んで目にした呪文。noImplicitAny。

Over time, they reached the point of enabling advanced compiler options, such as –noImplicitAny to prevent the compiler from inferring an any type.

advancedってすごいな。なるほど。やってみましょう。また、ドキュメントを見ると、strictNullChecksなるものも。

// tsconfig.json
{
    "compilerOptions": {
        "noImplicitAny": true,
        "strictNullChecks": true,
        "module": "commonjs",
        "target": "es5",
        "jsx": "react",
        "lib": ["es6", "dom"],
        "types": ["webpack-env", "jest"]
    }
}

ちょいちょい「暗黙のany」を怒るエラーがでてきました。たとえばこんなの。

export class Counter extends React.Component<{}, { counter: number }> {
    constructor(props) {
        super(props);
        this.state = { counter: 0 };
    }
// 省略
}

このconstructorの引数に型が無い。直すと以下。

export class Counter extends React.Component<{}, { counter: number }> {
    constructor(props: {}) {
        super(props);
        this.state = { counter: 0 };
    }
// 省略
}

結構な箇所を直したら、noImplicitAny=trueでもOKになった。明示的なanyはOK。

import { handleActions } from 'redux-actions';
import { LOCATION_CHANGE } from 'react-router-redux';
import { matchPath } from 'react-router';
import { Map } from 'immutable';

const decodeName = (pathname: string) => {
    const m = matchPath<{ name?: string; }>(pathname, { path: '/player/:name' });
    if (m) {
        return { ...m.params, name: m.params.name };
    }
    return {};
};

type AppState = Map<string, any>;

export const app = handleActions<AppState, any>(
    {
// 省略
        MOVE_TO: (state: AppState, { payload: { name } }) => {
            return state.merge({ name });
        },
        [LOCATION_CHANGE]: (state: AppState, { payload: { pathname } }) =>
            state.merge(decodeName(pathname)),
    },
    Map({}),
);

Redux ActionsのhandleActionsの型引数の2番目には、Payloadがきます。でもこれはMOVE_TOのときには、{ name } だし、LOCATION_CHANGEのときは{ pathname }だから、anyで受けたかった。で、明示しているからOKだった。

React RouterのmatchPathは面白くて、型引数にExpressスタイルのURL分解パラメータをあらかじめ型として持つ。'/player/:name'の結果を{ name?: string; }で受けたために、m.params.nameという取り方ができるようになる。

strictNullChecksは結構直すの簡単ながら、なるほどそこでnullの可能性あるかと気がつかしてくれる。とても有用。

結論

noImplicitAnyはtrueにするのが正義。strictNullChecksはさらにもっと実利がある。ほか、noImplicitThisやalwaysStrictも含めて、単にstrict=trueにすれば一番厳しい。

TypeScript 2.3 · TypeScript