The issue is that you are not using the switch
statement correctly. If you have the following:
switch(A) {
case B:
return doOne();
case C:
return doTwo();
default:
doThree();
}
it can be represented by the following if/else
chain:
if (A == B) {
return doOne();
} else if (A == C) {
return doTwo();
} else {
return doThree();
}
So the argument you give to switch
is always checked against the values given to each case
. Since you have switch(path)
then the expectation is that each of the case
statements will hold a value you want to compare directly to path
but you have an expression that returns a boolean, which is why the compiler stops you from comparing them.
You can do what you want if you invert the expectations of how switch
is supposed to be used. Instead of giving switch
a value and trying to match it with a case
, you can start with switch(true)
and give each case
an expression that produces a boolean:
const determinLayout = (path: string) => {
const adminPaths = ['/admin', '/finance'];
const marketingPaths = ['/marketing', '/deals'];
switch (true) {
case (adminPaths.indexOf(path) > -1):
return {
layout: 'admin',
};
case (marketingPaths.indexOf(path) > -1):
return {
layout: 'marketing',
};
default:
return {
layout: 'visitor',
};
}
};
TypeScript Playground link
However, this can start becoming hard to maintain if you add more case
s. It's also harder to read by other developers and it's harder to prove that you will match exactly one case
or not. Overall, it's not recommended to use it but in rare occasions it is useful.
Instead of this, you can re-structure the code similar to multiple dispatch but in order to return exactly one thing:
//declare an interface for each configuration item
interface PathConfig {
layout: string,
matchingPaths: string[]
}
const determinLayout = (path: string) => {
//have a collection of all
const pathsConfig: PathConfig[] = [
{ layout: "admin", matchingPaths: ['/admin', '/finance'] },
{ layout: "marketing", matchingPaths: ['/marketing', '/deals'] }
];
//attempt to find a configuration where `path` is matching
const config = pathsConfig.find(({ matchingPaths }) => matchingPaths.indexOf(path) > -1);
//if found return the corresponding layout
if (config) {
return {
layout: config.layout
};
}
//otherwise return a default
return {
layout: 'visitor',
};
};
TypeScript Playground link
The good thing here is that now the algorithm for finding and returning an item is entirely divorced from the configuration. You can add or remove configuration items and you don't need to change how you try to retrieve them, whereas with a switch
you need to add/remove a case
every time. You can even externalise pathsConfig
and load it from a file or elsewhere, which will enable you to modify the items there at runtime. You may not need it, but it's a benefit nonetheless.
The whole thing can be shortened using destructuring and default values:
//declare an interface for each configuration item
interface PathConfig {
layout: string,
matchingPaths: string[]
}
// assume these are loaded already
declare const pathsConfig: PathConfig[];
declare const defaultLayout: string;
//...
const determinLayout = (path: string) => {
//attempt to find a configuration where `path` is matching
//destructure to get `layout` or use the default
const { layout = defaultLayout } = pathsConfig
.find(({ matchingPaths }) => matchingPaths.indexOf(path) > -1) || {};
//return the corresponding layout
return { layout };
};
TypeScript Playground link