Vue3.4.27、Nuxt3.12.1で開発しようとしてuseRoute().query.hogeを変数に入れようとすると、型エラーが...

LocationQueryValue? 見慣れない型だな![]()
Vue3.4.15、Nuxt3.11.2で開発していた頃には見かけなかったので、ここから先述のバージョンの間に更新されたものと思われます。
確認したところ、↓こんな感じの型関係になっているようです。
export declare function useRoute(): RouteLocationNormalizedLoaded;
// ↓
export declare interface RouteLocationNormalizedLoaded extends _RouteLocationBase {
matched: RouteLocationMatched[];
}
// ↓
export declare interface _RouteLocationBase extends Pick<MatcherLocation, 'name' | 'path' | 'params' | 'meta'> {
// 省略
query: LocationQuery;
// 省略
}
// ↓
export declare type LocationQuery = Record<string, LocationQueryValue | LocationQueryValue[]>;
// ↓
export declare type LocationQueryValue = string | null;
なので、useRoute().query.hogeみたいに取得すると最終的には
string | null | (string | null)[]型が取得できます。
ここで困ったことが2点あります。
-
LocationQuery型はRecordなので存在しないキーを指定するとundefinedが返ってきますが、useRoute().query.hogeの型推論はLocationQueryValue | LocationQueryValue[]になっているためundefinedが含まれない - queryの値が配列である場合も考慮されていますが、私の場合配列で渡すことはない想定なので、毎回配列である可能性を除外して扱わないといけない
上記の対策コードを各query使用箇所に書くのはかなり冗長なので、Nuxtのutilsで定義しました。
import { type LocationQueryValue } from "vue-router";
export default (
queryValue: LocationQueryValue | LocationQueryValue[] | undefined
): string | undefined =>
queryValue instanceof Array
? queryValue[0] ?? undefined
: queryValue ?? undefined;
やっていることは簡単で、
1.配列なら配列の最初の要素を返す
※今回の私のコードだと配列は使わない想定なので「配列の最初の要素」というのは適当です
(使わないならundefinedだけ返すようにした方が良いかもしれない)
arr[0]の返り値はT | undefinedなので、queryValue[0]はstring | null | undefinedとなります。
これを?? undefinedすることで、nullの場合もundefinedに統一して返すようにしています。
2.配列でないならLocationQueryValueを返すが、この型の中身はstring | nullなので、1と同じく?? undefinedしてnullの場合でもundefinedで返す
です。
const hoge = extractRouteQueryStr(useRoute().query.hoge);
上記コードは配列の場合を一切考慮していないので、配列の場合どう返すかや、undefinedではなくnullで返したいなどの場合は適宜カスタマイズが必要です。
私の場合は、queryの値を受け取る関数を以下のような感じで定義していて、
nullをそのままにすると型定義が煩雑になるし、nullとundefindで関数の動作は変わらないので一律undefinedでいいかなという判断です。
const hoge = (
redirect?: string,
fuga: number,
foo: string,
) => {
//
}
// nullも考慮するとこうなる
const hoge = (
redirect?: string | null
fuga: number,
foo: string,
) => {
//
}
もしもっといいやり方があったら教えてください!
