这不是一个直接的答案,而是一个改变问题的提议。
这些函数的预期用法很好,但需要一个不容易维护的类型。使用具有可区分联合类型的整个对象参数怎么样?
// Routes discriminated by name
type HomeRoute = { name: 'Home' };
type PageRoute = { name: 'Page'; id: number };
type SearchRoute = { name: 'Search'; text: string; limit?: number };
type Route = HomeRoute | PageRoute | SearchRoute;
function navigateV1(route: Route): void { /*...*/ }
navigateV1({ name: 'Home' })
navigateV1({ name: 'Page', id: 1 })
navigateV1({ name: 'Page' }) // Got expected error "Property 'id' is missing..."
navigateV1({ name: 'Search', text: 'A*' })
用法只是有点冗长,但也给出了更详细的错误。
--
顺便说一句,使用上述区分联合类型,为了坚持所需的用法,我们还需要复杂的实用程序类型:
// V2 (more complex to maintain) : arguments as deconstructed object
type RouteName = Route['name']; // "Home" | "Page" | "Search"
type RouteArgs<TRouteName extends RouteName,
TRoute = Extract<Route, { name: TRouteName }>,
TRest = Omit<TRoute, 'name'>> =
{} extends TRest
? [TRouteName]
: [TRouteName, TRest];
type TestHomeRouteArgs = RouteArgs<'Home'>; // ["Home"]
type TestPageRouteArgs = RouteArgs<'Page'>; // ["Page", Pick<PageRoute, "id">]
type TestSearchRouteArgs = RouteArgs<'Search'>; // ["Search", Pick<PageRoute, "text" | "limit">]
function navigateV2<TRouteName extends RouteName>(...args: RouteArgs<TRouteName>): void { /*...*/ }
navigateV2('Home')
navigateV2('Page', { id: 1 })
navigateV2('Page') // Got expected error but less precise: "Expected 2 arguments but got 1"
navigateV2('Search', { text: 'A*' })
// ----
// V3 : even more complex but output types more readable
type Prettify<T> = T extends infer Tbis ? { [K in keyof Tbis]: Tbis[K] } : never;
type PrettyRouteArgs<TRouteName extends RouteName,
TRoute = Extract<Route, { name: TRouteName }>,
TRest = Omit<TRoute, 'name'>> =
{} extends TRest
? [TRouteName]
: [TRouteName, Prettify<TRest>];
type TestPrettyHomeRouteArgs = PrettyRouteArgs<'Home'>; // ["Home"]
type TestPrettyPageRouteArgs = PrettyRouteArgs<'Page'>; // ["Page", { id: number; }]
type TestPrettySearchRouteArgs = PrettyRouteArgs<'Search'>; // ["Search", { text: string; limit?: number | undefined; }]
function navigateV3<TRouteName extends RouteName>(...args: PrettyRouteArgs<TRouteName>): void { /*...*/ }
navigateV3('Home')
navigateV3('Page', { id: 1 })
navigateV3('Page') // Got same expected error
navigateV3('Search', { text: 'A*' })