Open15

TypeScript/tsconfig.jsonのtarget, moduleなどをどう設定すべきか

ak2ieak2ie

TypeScriptでトップレベルawaitを使おうとしたら、エラーが出た

トップレベルの 'await' 式は、'module' オプションが 'es2022'、'esnext'、'system'、'node16' または 'nodenext' に設定されていて、'target' オプションが 'es2017' 以上に設定されている場合にのみ使用できます。ts(1378)
ak2ieak2ie

エラーが出たときの環境

$ tsc --version
Version 5.3.3
tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    // ...
  }
}
package.json
{
  "main": "index.js",
  "license": "MIT",
  // typeは未記載
  // ...
}
ak2ieak2ie

tsconfig.json"module": "ESNext"を、package.json"type": "module"を指定することでESModuleとして動作させることにした

ak2ieak2ie

importで拡張子まで含めたファイルパスを指定する必要があった

// Cannot find module './routes'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?ts(2792)
import router from "./routes";

// 内部的にはJavaScriptなので、拡張子は".js"
import router from "./routes/index.js";

ak2ieak2ie

ライブラリのインポートでエラーになった

Cannot find module '@vendia/serverless-express'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?ts(2792)
import serverlessExpress from "@vendia/serverless-express";

エラーメッセージの通り、tsconfig.json"moduleResolution": "NodeNext"を追記したら、今度はmoduleがダメと言われたので"module": "NodeNext",に修正

tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    // Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext'.ts
    "module": "ESNext",
   ↓
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
ak2ieak2ie

まだ、トップレベルawaitのエラー消えないと思ったら、tsconfig.jsontargetの修正が漏れてた

{
  "compilerOptions": {
    // (前略)'target' オプションが 'es2017' 以上に設定されている場合にのみ使用できます。ts(1378)
    "target": "es6",
   ↓
     "target": "es2022",
ak2ieak2ie

別の箇所でエラーが出る

import serverlessExpress from "@vendia/serverless-express";
// この式は呼び出し可能ではありません。
//  型 'typeof import("/.../node_modules/@vendia/serverless-express/src/index")' には呼び出しシグネチャがありません。ts(2349)
export const handler = serverlessExpress({ app });
ak2ieak2ie

どうも呼び出し方が間違っていたっぽい?

node_modules/@codegenie/serverless-express/src/index.d.ts
// 型定義
import configure from "./configure"

export default configure;
export { default as configure } from "./configure"
export { getCurrentInvoke } from "./current-invoke"
node_modules/@codegenie/serverless-express/src/configure.d.ts
// 上でdefaultとしてexportされていたconfigureの定義

// (前略)
interface ConfigureParams {
  app: RequestListener;    // <========= もともと渡していたのはこれ?
  logSettings?: LogSettings;
  log?: Logger;
  framework?: Framework;
  binaryMimeTypes?: string[];
  binarySettings?: BinarySettings;
  resolutionMode?: string;
  eventSourceName?: string;
  eventSource?: EventSource; // TODO:
  eventSourceRoutes?: { [key in EventSources]?: string };
  respondWithErrors?: boolean;
}

interface BinarySettings {
  isBinary?: Function | boolean;
  contentTypes?: string[];
}

export interface ConfigureResult<TEvent = any, TResult = any> {
  handler: Handler<TEvent, TResult>;
  log: Logger;
  proxy: (proxyParams: ProxyParams) => Promise<Object>;
}

declare function configure<TEvent = any, TResult = any>(configureParams: ConfigureParams): Handler<TEvent, TResult> & ConfigureResult<TEvent, TResult>;

// declare function proxy(proxyParams: ProxyParams): Promise<any>

export default configure;

明示的にconfigureを呼び出すようにしたら、エラーは消えた

index.ts
export const handler = serverlessExpress.configure({ app });
ak2ieak2ie

importで拡張子まで書かなくても良くするには、tsconfig.json"moduleResolution": "Bundler"をしていすればよいらしい。

ただ、moduleには`ES2015以降を指定する必要がある

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
     // Option 'bundler' can only be used when 'module' is set to 'es2015' or later.ts
    "moduleResolution": "Bundler"

ak2ieak2ie

moduleES2022を指定したら、修正したserverlessExpressの呼び出しで怒られた
元の呼び出し方に戻した

index.ts
// プロパティ 'configure' は型 '<TEvent = any, TResult = any>(configureParams: ConfigureParams) => Handler<TEvent, TResult> & ConfigureResult<TEvent, TResult>' に存在しません。ts(2339)
export const handler = serverlessExpress.configure({ app });
//  ↓
export const handler = serverlessExpress({ app });
ak2ieak2ie

"moduleResolution": "Bundler"tsc以外ツールでビルドする場合に利用するものらしい
今回はtscを使っているため、"moduleResolution": "Node16"に戻した

ak2ieak2ie

実行したらエラーが出た

[0] TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /home/ak2ie/kintone_line_case_management/src/index.ts
[0]     at new NodeError (node:internal/errors:405:5)
[0]     at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:99:9)
[0]     at defaultGetFormat (node:internal/modules/esm/get_format:142:36)
[0]     at defaultLoad (node:internal/modules/esm/load:91:20)
[0]     at DefaultModuleLoader.load (node:internal/modules/esm/loader:263:26)
[0]     at DefaultModuleLoader.moduleProvider (node:internal/modules/esm/loader:179:22)
[0]     at new ModuleJob (node:internal/modules/esm/module_job:63:26)
[0]     at DefaultModuleLoader.#createModuleJob (node:internal/modules/esm/loader:203:17)
[0]     at DefaultModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:156:34)
[0]     at DefaultModuleLoader.getModuleJob (node:internal/modules/esm/loader:141:17) {
[0]   code: 'ERR_UNKNOWN_FILE_EXTENSION'
[0] }

ts-nodeで実行してたけど、オプション追加が必要らしい

ts-node --esm src/index.ts

https://qiita.com/nyanchu/items/82903e0463fa9d558639