routing-controllersを使用したNode.jsアプリでtscをts-nodeに切り替えるとParameterParseJsonError.HttpError

# 現象

Node.js (Express & routing-controllers & TypeORM)プロジェクトを、`tsc`でビルドして起動から`ts-node`直接起動に変えると下記のエラーが発生する

```
Error
    at ParameterParseJsonError.HttpError [as constructor] (c:\workspace\liberapp.net\src\http-error\HttpError.ts:19:22)
    at ParameterParseJsonError.BadRequestError [as constructor] (c:\workspace\liberapp.net\src\http-error\BadRequestError.ts:10:9)
    at new ParameterParseJsonError (c:\workspace\liberapp.net\src\error\ParameterParseJsonError.ts:10:9)
    at ActionParameterHandler.parseValue (c:\workspace\liberapp.net\node_modules\routing-controllers\ActionParameterHandler.js:151:15)
    at ActionParameterHandler.normalizeParamValue (c:\workspace\liberapp.net\src\ActionParameterHandler.ts:163:15)
    at ActionParameterHandler.handle (c:\workspace\liberapp.net\src\ActionParameterHandler.ts:40:19)
    at c:\workspace\liberapp.net\src\RoutingControllers.ts:116:49
    at Array.map (<anonymous>)
    at RoutingControllers.executeAction (c:\workspace\liberapp.net\src\RoutingControllers.ts:116:14)
    at c:\workspace\liberapp.net\src\RoutingControllers.ts:83:33
(node:9720) UnhandledPromiseRejectionWarning: Error
    at AuthorizationRequiredError.HttpError [as constructor] (c:\workspace\liberapp.net\src\http-error\HttpError.ts:19:22)
    at AuthorizationRequiredError.UnauthorizedError [as constructor] (c:\workspace\liberapp.net\src\http-error\UnauthorizedError.ts:10:9)
    at new AuthorizationRequiredError (c:\workspace\liberapp.net\src\error\AuthorizationRequiredError.ts:12:9)
    at c:\workspace\liberapp.net\src\ActionParameterHandler.ts:98:10
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:228:7)
```

# 旧コード

```ts:controller.ts
  @Post("/changeApplicationStatus")
  async updateStatus(
    @CurrentUser({ required: true }) user: User,
    @BodyParam("applicationId", { required: true }) applicationId: number,
    @BodyParam("status", { required: true }) status: ApplicationStatus
  ) {
    return jsonApiResult(
      await Applications.updateStatus(user.publisherId, applicationId, status)
    );
  }
```

```ts:entitiy.ts
export enum ApplicationStatus {
  PublicReleased = "PublicReleased",
  LimitedReleased = "LimitedReleased",
  Private = "Private",
  Archived = "Archived",
  Banned = "Banned"
}
```

```ts:gulp
gulp.task(
  "start-dev",
  gulp.series("build", gulp.parallel("build", "start-nodemon"))
);

gulp.task("start-nodemon", function() {
  nodemon({
  script: "./dist/www",
  env: { NODE_ENV: "development", DEBUG: "*:*" },
  delay: 3000
});
});

gulp.task("build", () => {
  return gulp
    .src(["./src/**/*.ts"])
    .pipe(tsProject())
    .pipe(gulp.dest("./dist"));
});

```

```ts:tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "sourceMap": true,
    "types": ["node"],
    "rootDirs": ["src"],
    "outDir": "dist"
  },
  "include": ["src/**/*.ts"],
  "exclude": ["dist","node_modules"]
}
```

# 新コード

```ts
  @Post("/changeApplicationStatus")
  async updateStatus(
    @CurrentUser({ required: true }) userSession: UserSession,
    @BodyParam("applicationId", { required: true }) applicationId: number,
    @BodyParam("status", { required: true }) status: ApplicationStatus
  ) {
    getCustomRepository(ApplicationRepository).updateStatus(
      applicationId,
      status
    );

    return jsonApiResult(
      await Applications.updateStatus(
        userSession.currentPublisherId,
        applicationId,
        status
      )
    );
  }
```

```ts
export enum ApplicationStatus {
  PublicReleased = "PublicReleased",
  LimitedReleased = "LimitedReleased",
  Private = "Private",
  Archived = "Archived",
  Banned = "Banned"
}
```

```ts:gulp.js
gulp.task(
  "start-dev",
  function() {
  nodemon({
    watch: ["./src"],
    ext: "ts",
    exec: "./node_modules/.bin/ts-node ./src/www.ts",
    env: { NODE_ENV: "development", DEBUG: "*:*" },
    delay: 3000
  });
});
```

```ts:tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "sourceMap": true,
    "types": ["node"],
    "rootDirs": ["src"],
    "outDir": "dist"
  },
  "include": ["src/**/*.ts"],
  "exclude": ["dist""node_modules"]
}
```

# 調査

`routing-controllers`にダンプするコードを埋める

https://github.com/typestack/routing-controllers/blob/master/src/ActionParameterHandler.ts

```js:ActionParameterHandler.js
  ActionParameterHandler.prototype.normalizeParamValue = function(
    value,
    param
  ) {
++ console.log(value, ",", param);
```

## 旧コード

```ts

PublicReleased,

ParamMetadata {
  targetName: 'string',
  isTargetObject: true,
  actionMetadata:
   ActionMetadata {
     options:
      { controllers: [Array],
        authorizationChecker: [Function: authorizationChecker],
        currentUserChecker: [Function: currentUserChecker] },
     controllerMetadata:
      ControllerMetadata {
        target: [Function: ApiController],
        route: '/api',
        type: 'json',
        isAuthorizedUsed: false,
        authorizedRoles: [],
        actions: [Array],
        uses: [],
        interceptors: [Array] },
     route: '/changeApplicationStatus',
     target: [Function: ApiController],
     method: 'updateStatus',
     type: 'post',
     appendParams: undefined,
     methodOverride: undefined,
     params: [ [Object], [Object], [Circular] ],
     uses: [],
     interceptors: [],
     undefinedResultCode: undefined,
     nullResultCode: undefined,
     bodyExtraOptions: undefined,
     isBodyUsed: true,
     isFilesUsed: false,
     isFileUsed: false,
     isJsonTyped: true,
     fullRoute: '/api/changeApplicationStatus',
     headers: {},
     isAuthorizedUsed: false,
     authorizedRoles: [] },
  target: [Function: ApiController],
  method: 'updateStatus',
  extraOptions: undefined,
  index: 2,
  type: 'body-param',
  name: 'status',
  parse: undefined,
  required: true,
  transform: undefined,
  classTransform: undefined,
  validate: undefined,
  targetType: [Function: String] }
```
# ts-node書き換え後のコード

```ts
 LimitedReleased,
 ParamMetadata {
  targetName: 'object',
  isTargetObject: true,
  actionMetadata:
   ActionMetadata {
     options:
      { controllers: [Array],
        authorizationChecker: [Function: authorizationChecker],
        currentUserChecker: [Function: currentUserChecker] },
     controllerMetadata:
      ControllerMetadata {
        target: [Function: ApiController],
        route: '/api',
        type: 'json',
        isAuthorizedUsed: false,
        authorizedRoles: [],
        actions: [Array],
        uses: [],
        interceptors: [Array] },
     route: '/changeApplicationStatus',
     target: [Function: ApiController],
     method: 'updateStatus',
     type: 'post',
     appendParams: undefined,
     methodOverride: undefined,
     params: [ [Object], [Object], [Circular] ],
     uses: [],
     interceptors: [],
     undefinedResultCode: undefined,
     nullResultCode: undefined,
     bodyExtraOptions: undefined,
     isBodyUsed: true,
     isFilesUsed: false,
     isFileUsed: false,
     isJsonTyped: true,
     fullRoute: '/api/changeApplicationStatus',
     headers: {},
     isAuthorizedUsed: false,
     authorizedRoles: [] },
  target: [Function: ApiController],
  method: 'updateStatus',
  extraOptions: undefined,
  index: 2,
  type: 'body-param',
  name: 'status',
  parse: undefined,
  required: true,
  transform: undefined,
  classTransform: undefined,
  validate: undefined,
  targetType: [Function: Object] }
```

# 解決

コチラの記事のコレで解決
https://github.com/TypeStrong/ts-node/issues/515

> Running ts-node in type check mode should fix this problem:
> `$ TS_NODE_TYPE_CHECK=Y ts-node`


```diff:gulp.js
gulp.task(
  "start-dev",
  function() {
  nodemon({
    watch: ["./src"],
    ext: "ts",
    exec: "./node_modules/.bin/ts-node ./src/www.ts",
-    env: { NODE_ENV: "development", DEBUG: "*:*" },
+    env: { NODE_ENV: "development", DEBUG: "*:*", TS_NODE_TYPE_CHECK: "Y" },
    delay: 3000
  });
});
```