型キャストと型アサーション 検証やあれこれ

C#の文献ですがキャストとアサーションの概念はTypeScript以外でも同じみたいでした。

検索: 型キャスト コスト

C# Tips -キャストすべきかasするべきか-

1Number(T) 
2String(K)
3Boolean(L)

以上がJavaScriptの持つ型変換機能で、

1as number
2as string
3as boolean

以上がTypeScriptの持つ、コンパイラーの型推論を上書きする機能。 こちらには型自体を変換する機能はない。 型系統が違う無茶な上書きをコンパイラに認識させる場合 as unknown as stringのようにunknownを経由してエラーを出さないようにできる。

ただこの書き方は個人的にTypeScriptの機能の中ではかなり微妙だと思っている。

基本的にアサーションは型の変換はできなく、コンパイラの推論を上書きする機能になり、キャストは型を文字通り明示的に変化させる。

Contentful API clientの検証で試してみた

検証キャスト組

1  space: String(process.env.CONTENTFUL_SPACE_ID),
2  accessToken: String(process.env.CONTENTFUL_DELIVERY_TOKEN),
1  space: (`${process.env.CNTENTFUL_SPACE_ID}`),
2  accessToken: (`${process.env.CONTENTFUL_DELIVERY_TOKEN}`),

中身を間違えてエラーを出させた

1> Build error occurred
2NotFound: {
3  "status": 404,
4  "statusText": "Not Found",
5  "message": "The resource could not be found.",
6  "details": {},
7  "request": {
8    "url": "entries",
9    "headers": {
10      "Accept": "application/json, text/plain, */*",
11      "Content-Type": "application/vnd.contentful.delivery.v1+json",
12      "X-Contentful-User-Agent": "sdk contentful.js/9.1.10; platform node.js/v16.16.0; os macOS/v16.16.0;",
13      "Authorization": "Bearer ...NDtWg",
14      "user-agent": "node.js/v16.16.0",
15      "Accept-Encoding": "gzip"
16    },
17    "method": "get"
18  },
19  "requestId": "a62998fe-6630-4039-8c0f-6ec9b155349d"
20}
21    at errorHandler (/Users/sou/FrontEnd/sou-web.net/node_modules/contentful/dist/contentful.node.js:3783:15)
22    at Object.getEntries (/Users/sou/FrontEnd/sou-web.net/node_modules/contentful/dist/contentful.node.js:8478:79)
23    at processTicksAndRejections (node:internal/process/task_queues:96:5)
24    at async getStaticPaths (/Users/sou/FrontEnd/sou-web.net/.next/server/pages/docs/[id].js:156:21)
25    at async buildStaticPaths (/Users/sou/FrontEnd/sou-web.net/node_modules/next/dist/build/utils.js:497:31)
26    at async /Users/sou/FrontEnd/sou-web.net/node_modules/next/dist/build/utils.js:640:119
27    at async Span.traceAsyncFn (/Users/sou/FrontEnd/sou-web.net/node_modules/next/dist/trace/trace.js:75:20) {
28  type: 'Error'
29}

検証アサーション組

1  space: process.env.CONTENTFUL_SPACE_ID as string,
2  accessToken: process.env.CONTENTFUL_DELIVERY_TOKEN as string,
1  space: <string>process.env.CONTENTFUL_SPACE_ID,
2  accessToken: <string>process.env.CONTENTFUL_DELIVERY_TOKEN,
1  space: process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID || "",
2  accessToken: process.env.NEXT_PUBLIC_CONTENTFUL_DELIVERY_TOKEN || "",

こちらも同様エラーを出させた

1> Build error occurred
2TypeError: Expected parameter space
3    at createClient (/Users/sou/FrontEnd/sou-web.net/node_modules/contentful/dist/contentful.node.js:8154:11)
4    at Object.2722 (/Users/sou/FrontEnd/sou-web.net/.next/server/pages/docs.js:51:68)
5    at __webpack_require__ (/Users/sou/FrontEnd/sou-web.net/.next/server/webpack-runtime.js:25:42)
6    at /Users/sou/FrontEnd/sou-web.net/.next/server/pages/docs.js:71:73
7    at Function.__webpack_require__.a (/Users/sou/FrontEnd/sou-web.net/.next/server/webpack-runtime.js:103:13)
8    at Object.6820 (/Users/sou/FrontEnd/sou-web.net/.next/server/pages/docs.js:63:21)
9    at __webpack_require__ (/Users/sou/FrontEnd/sou-web.net/.next/server/webpack-runtime.js:25:42)
10    at __webpack_exec__ (/Users/sou/FrontEnd/sou-web.net/.next/server/pages/docs.js:273:39)
11    at /Users/sou/FrontEnd/sou-web.net/.next/server/pages/docs.js:274:74
12    at Function.__webpack_require__.X (/Users/sou/FrontEnd/sou-web.net/.next/server/webpack-runtime.js:190:21) {
13  type: 'TypeError'
14}

型キャストした方はリクエストのGET methodまで進行しているのに対し 型アサーションした方はTypeErrorなのでundefinedが効いている

キャストとas演算子の使い分 [引用]

基本的には、 キャストに成功することが前提であるならキャストを使う。 キャストに成功するかどうかわからないときはas演算子を使う。 という感じでいいと思ってます。

この記事の方に沿ってするとすれば容易に型変換が成功する場合はキャストで明示的な(暗黙でない)型変換は使うといいみたいなことが書かれている。

ただundefinedを含んでる値はキャストした場合強制的にundefinedをスローしないまま通してしまうことがわかった、つまりタイプミスでエラーをスローできなくなるからキャストしない方が良さそうだ。

API SDKの場合値がundefinedの場合GET methodまで進んでしまうことがわかったのでアサーションで良さそうだ。

アサーションの場合 a = bとして aはbの扱いはされるけど実態はaのままでキャストは a = bは aの中身ははbという実態に完全に置き換わる 今回判明したことが||""とするのは型アサーションと同じ結果をもたらして`${}`とするとキャストと同じ結果をもたらしたことだ。

アサーションは環境変数のnext_public_を付けないと500Internal Server Error.が出た。 キャストはnext_public_を付けなくてもAPIが取得できたが、いいエラーを出すためにundefinedの択があった方が絶対いいだろう。

ちなみに同時にnodemailerもnext_publicで試してみたら本番環境ではnext_publicを付けないと動作しなかった。

next exportでも動作しないしコンテナかつnext_publicでようやく本番動作する。 コンテナでデプロイするメリットはISRが使える点、Nodemailerが快適に使える点、だろうか。 参考: Next.jsのIncremental Static RegenerationをVercel以外でやってみる