前提
- エディタはVSCode
- ts-serverの力によるため
- やむを得ない事情で、どうしてもJavaScriptで頑張らないといけない
何故やるのか
- APIの応答値の一部を抜粋したものを扱うときに都合がいい
- APIの応答値の型が変わった際に、ReturnTypeで引っ張ってきていれば予期せぬ不整合が発生しにくい
- ReturnTypeを使わず構造の一部をそのまま転記した場合、APIの応答値の構造が変わった時にコードだけではなくJSDocも修正して回る必要が発生する
- JSDocは型アノテーションで型推論結果より優先されてしまうので、不整合に気づきにくい
- 都度構造を書いて回るより、「ここの構造参照」としたほうがいろんなときに楽
- 読むとき
- 書くとき
使うもの
- Indexed Access Types
- Utility Types
- ReturnType
やること
以下のような、入れ子になった無名オブジェクトを返すような関数を想定しています。
function testFunction() { return { a: { aa: { aaa: 1, bbb: ["aaa"] } }, b: { bb: { bbb: true } } }; }
まずReturnTypeで戻り値の型に別名をつけます
/** @typedef {ReturnType<testFunction>} testFunctionReturnType */
次にIndexed Access Typesで一部を取得し、別名をつけます 参照する型のプロパティを入力する際、[]の中に"を入力した時点でプロパティ一覧が提示されますので、やたら複雑でも間違える心配はありません。
/** @typedef {testFunctionReturnType["a"]["aa"]} testFunctionReturnType_A_AA*/
@typeで型を指定して適当な変数名を宣言します。
/** @type {testFunctionReturnType_A_AA} */ const test = {};
エディタ上での型の表示結果は以下です。
//@ts-check
をどこかに入れておけば、型情報に基づいてエラーを出してくれます。
Promiseでくるまれている場合
UtilityTypeのAwaited
を使うことでunwrapできます。
unwrapしたら以降は同様に扱えます。
function testPromiseFunction() { return Promise.resolve({ a: { aa: { aaa: 1, bbb: ["aaa"] } }, b: { bb: { bbb: true } } }); } /** @typedef {Awaited<ReturnType<testFunction>>} testFunctionReturnType */