Вопрос конечно в духе "я его того, а он мне нихрна, чаво это он". Где конкретика блин? Что именно за ошибка? Любая ошибка TS - это "ошибка типов", он для этого и существует. Что у тебя в
route
?
Но окей, предположим, что
route
у тебя - это текущий роут vue-рутера, тогда ошибка у тебя скорее всего выглядит как-то так:
TS7053: Element implicitly has an 'any' type because expression of type 'RouteRecordName' can't be used to index type '{ first: string; second: string; }'. No index signature with a parameter of type 'string' was found on type '{ first: string; second: string; }'.
Собственно прочитав текст ошибки можно уже догадаться, в чём проблема: у
(неявно выведенного за отсутствием явного объявления) типа объекта
pages
есть ключи типа
'first' | 'second' | ...
, но нет
index signature типа
string
, т.е.
не указано, что ключом может быть
любая строка, а не только конкретные
'first' | 'second' | ...
.
route.name
же после проверки на пустоту имеет тип
string | symbol
. Ты не можешь у объекта с чётко ограниченным набором ключей брать значение по произвольному строковому/символьному.
Прямое решение в лоб:
Задать тип
pages
позволяющий рандомные ключи, например
const pages: Record<PropertyKey, string> = {
first: 'Первая',
second: 'Вторая',
...
}
Всё сразу заработает, но это не спасёт тебя от ошибок(например опечаток).
Не менее прямой вариант(но с другой стороны):
Кастануть нужный тип руками:
... = pages[route.name as keyof typeof pages]
Ведь мы уверены, что
name
в
route
всегда будет одним из ключей
pages
. Уверены же?..
Энтерпрайз решение(ничем не лучше предыдущих, зато выглядит солидно):
Твой файл routes.ts:
export enum ERoutes {
FIRST = 'first',
SECOND = 'second'
}
const routes = [
{
name: ERoutes.FIRST,
...
},
{
name: ERoutes.SECOND,
...
},
...
]
В коде:
const pages: Record<ERoutes, string> = {
[ERoutes.FIRST]: 'Первая',
[ERoutes.SECOND]: 'Вторая',
...
}
... = pages[route.name as ERoutes]
Надмозговое решение("как батька"):
Твой файл routes.ts:
const routes = [
{
name: 'first',
...
},
{
name: 'second',
...
},
...
] as const satisfies ReadonlyArray<ReadonlyRouteRecordRaw>;
type ReadonlyRouteRecordRaw = Omit<RouteRecordRaw, 'children'> & {
children?: ReadonlyArray<ReadonlyRouteRecordRaw>;
};
type ExtractNames<Route> = Route extends { name: infer Name } ? Name : never;
type FlattenChildren<Route> = Route extends { children: ReadonlyArray<infer Children> }
? FlattenChildren<Children> | Route
: Route;
// с помощью магии ts вытаскиваем в тип RouteNames все заданные у нас имена маршрутов
export type RouteNames = ExtractNames<FlattenChildren<typeof routes[number]>>;
// с помощью магии же прокидывем их прямо в декларацию vue-router
declare module 'vue-router' {
export interface RouteLocationNormalizedLoaded {
name: RouteNames | null | undefined;
}
}
satisfiessatisfies
- новая фича ts 4.9, в предыдущих версиях того же можно добиться сделав обёртку вида:
const narrowRoutesTypeWrapper = <T extends ReadonlyArray<ReadonlyRouteRecordRaw>>(routes: T) => routes;
const routes = narrowRoutesTypeWrapper([ ... ] as const);
И твой код заработает вообще без изменений (если в pages есть все нужные ключи).)
Однако для удобства можно написать так:
const pages: Record<RouteNames, string> = {
first: 'Первая',
second: 'Вторая',
...
}