ExpressのRequestとResponseがGenericsになった

Expressをもっとタイプセーフに

v4.17.2にてRequest型がジェネリクス型に、v4.17.3にてReponse型がジェネリクス型に型定義が変更されたので、どのようなものになったのか調べてみました。結論から先に言うと、Request型では名前付きルートパラメータの型を、Response型はレスポンスボディの型を型パラメータで指定できるようになりました。

Request

Request型の型定義は以下の通りです。

interface Request<P extends core.Params = core.ParamsDictionary> extends core.Request<P> { }
interface RequestHandler<P extends core.Params = core.ParamsDictionary> extends core.RequestHandler<P> { }
interface RequestParamHandler extends core.RequestParamHandler { }

型定義の実態は@types/express-serve-static-coreに書かれています。

export interface Request<P extends Params = ParamsDictionary, ResBody = any, ReqBody = any> extends http.IncomingMessage, Express.Request {
    body: ReqBody;
    params: P;
    query: any;
    res?: Response<ResBody>;
}

どうやらbodyparams(ルートパラメータ)の型をジェネリクスで指定できるみたいです。ただ、今のところアクセスできる型パラメータはparamsの型だけのようです。その型パラメータPにはParamsという型制約が設けらており、デフォルトはParamsDictionaryとなっています。

export interface ParamsDictionary { [key: string]: string; }
export type ParamsArray = string[];
export type Params = ParamsDictionary | ParamsArray;

stringの配列、もしくはstring型のインデックスシグネチャを型パラメータに指定できるようです。実際に使ってみると。

type RootParams = {
    id: string,
    class: string
}

app.get('/api/students/:id/:class', (req: Express.Request<RootParams>, res:Express.Response)=> {
    // paramsの型推論が可能となる
    console.log(req.params.id)
    console.log(req.params.class)
    res.sendStatus(200)
})

Response

export interface Response<ResBody = any> extends core.Response<ResBody> { }

型定義の実態は@types/express-serve-static-coreに書かれています。

export interface Response<ResBody = any> extends http.ServerResponse, Express.Response {
    /** 中略 **/
    send: Send<ResBody, this>;
    json: Send<ResBody, this>;
    jsonp: Send<ResBody, this>;
}

export type Send<ResBody = any, T = Response<ResBody>> = (body?: ResBody) => T;

Response型のジェネリクスはシンプルです。型パラメータに指定した型がsendメソッドやjsonメソッドの引数の型になります。すなわちレスポンスボディの型をジェネリクスで指定できるということですね。

interface Student {
    name: string,
    age: number
}

app.get('/api/students/:id', (req: Express.Request, res:Express.Response<Student>)=> {
    const student: Student = {
        name: 'nanoha',
        age: 9
    }

    res.status(200).send(student)
    // res.status(200).send('fate') ←Student interfaceに合致していない場合はエラー
})

さいごに

Request型はResBodyReqBodyという型パラメータが使えるようになると一気に使いやすくなりそうです。一方Response型は今すぐにでも効果が得られそうなので、どんどんレスポンスオブジェクトの型を縛っていけばいいんじゃないかなと思います。

Profile
d_yama
元Microsoft MVP for Windows Development(2018-2020)
Sub-category : Windows Mixed Reality
Search