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,
) => {
//
}
もしもっといいやり方があったら教えてください!