Skip to content

Instantly share code, notes, and snippets.

@kazuma1989
Created November 24, 2020 02:16
Show Gist options
  • Save kazuma1989/795f2d6eda9213a655bc4dede2674de9 to your computer and use it in GitHub Desktop.
Save kazuma1989/795f2d6eda9213a655bc4dede2674de9 to your computer and use it in GitHub Desktop.
補完の効くSPA内遷移先一覧を定義する方法
const paths = [
'/settings/:id/:tab',
'/profile/:tab(foo)',
'/profile/',
'/users/:userId',
'/',
] as const;
/**
* SPA 内遷移のパス一覧
*
* `Path[目的のパス]` で、"目的のパス" を返す関数が取得できる。
* その関数を引数なしで呼べばキーの値がそのまま返るので、react-router の Route path に渡す値として使える。
* 引数ありで呼べばパスパラメーター部分を置換した値が返るので、react-router の Link to として使える。
*
* @example
* Path['/users/:userId']() === '/users/:userId';
* Path['/users/:userId']({ userId: 'xxx' }) === '/users/xxx';
* Path['/settings/:id/:tab']({ id: 'xxx', tab: 'yyy' }) === '/settings/xxx/yyy';
* Path['/profile/:tab(foo)']({ 'tab(foo)': 'foo' }) === '/profile/foo';
*/
export const Path: {
[P in typeof paths[number]]: (param?: ParamObject<P>) => string;
} = Object.fromEntries(
paths.map(path => [
path,
(param?: ParamObject<typeof path>) =>
param
? path.replace(
/:([^/]+)/g,
(match, key: ParamNames<typeof path>) =>
param[key]?.toString() ?? match,
)
: path,
]),
) as any;
// https://twitter.com/Quramy/status/1300758740317081601?s=20
type ParamFromTemplate<T extends string> = T extends `:${infer P}` ? P : never;
type ParamNames<S extends string> = string extends S
? string
: S extends `${infer T}/${infer U}`
? ParamFromTemplate<T> | ParamNames<U>
: ParamFromTemplate<S>;
type ParamObject<T extends string> = {
[P in ParamNames<T>]?: string | number;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment