I am currently building a component that accepts some routes, and makes a stepper with a nested router view.
I'm having some trouble with satisfying TypeScript.
I am using vue-router
's RouteLocationRaw
type.
RouteLocationRaw
is a union of string
and two intersection types defined as
export declare type RouteLocationRaw = string | (RouteQueryAndHash & LocationAsPath & RouteLocationOptions) | (RouteQueryAndHash & LocationAsRelativeRaw & RouteLocationOptions);
export declare interface RouteQueryAndHash {
query?: LocationQueryRaw;
hash?: string;
}
export declare interface LocationAsPath {
path: string;
}
export declare interface RouteLocationOptions {
replace?: boolean;
force?: boolean;
state?: HistoryState;
}
export declare interface LocationAsRelativeRaw {
name?: RouteRecordName;
params?: RouteParamsRaw;
}
What I would like to do is compare the name of the current route against those passed into the component like so
const activeRoute = computed(() => props.routes.find((propRoute) => propRoute.name === route.name))
This logic works as I would like, but TypeScript complains. With the approach above I get the following errors.
Property 'name' does not exist on type 'RouteLocationRaw'.
Property 'name' does not exist on type 'string'.
TypeScript seems to automatically assume that the type is a string which is the first part of the union. Already kind of a strange thing to assume, but narrowing beyond a string isn't helpful either.
If I add a case for handling routes that are of type
string
TypeScript still doesn't recognize that name
could be a property of route
.
Property 'name' does not exist on type '(RouteQueryAndHash & LocationAsPath & RouteLocationOptions) | (RouteQueryAndHash & LocationAsRelativeRaw & RouteLocationOptions)'.
Property 'name' does not exist on type 'RouteQueryAndHash & LocationAsPath & RouteLocationOptions'.
CodePudding user response:
I'm going to simplify your code to this, which has the same error.
declare const propRoute: RouteLocationRaw // the current route, lets say
if (propRoute.name === 'foo') {
console.log('foo is active')
}
Now to access a property of any value, that type must declare that property. And when you access a property of a union, then all members of that union must declare that. So typescript is not assuming that the value is string
, it's telling you that it might be a string
and if it is, then this property access doesn't make any sense.
So, to access the name
property, you need to narrow the union to types that only support the name
property. So you have to filter out string
and you have to filter out an object property that lacks that property.
That might look like this:
if (
typeof propRoute !== 'string' && // filter out string.
'name' in propRoute && // require the `name` property.
propRoute.name === 'foo' // check the name proeprty.
) {
console.log('foo is active')
}