当类型从不时使参数可选
Make parameter optional when type is never
我想写一个给定路由和一组可选参数的函数 returns url 路由中的占位符替换为给定的参数。
参数的类型应该与路由中的占位符匹配,如果路由没有占位符,则参数应该是可选的。这是预期结果的示例:
type Route = '/no-placeholders' | '/path/:p1/:p2'
buildUrl('/unknown-route') // error, route does not exist
buildUrl('/no-placeholders', {}) // error, expected 1 param got 2
buildUrl('/no-placeholders')
buildUrl('/path/:p1/:p2') // error, expected 2 params, got 1
buildUrl('/path/:p1/:p2', { foo: '' }) // error, { foo: string } does not match { p1: string; p2: string }
buildUrl('/path/:p1/:p2', { p1: 'foo', p2: 'bar' })
所以,我得出的结果如下,重载在这里没有用,但我试图以不同的方式处理 never
的情况:
type PathParams<Path extends string> =
Path extends `:${infer Param}/${infer Rest}` ? { [k in Param | keyof PathParams<Rest>]: string } :
Path extends `:${infer Param}` ? { [k in Param]: string } :
Path extends `${infer _Prefix}:${infer Rest}` ? { [k in keyof PathParams<`:${Rest}`>]: string } :
never;
type Route = '/no-placeholders' | '/path/:p1/:p2'
function buildUrl<R extends Route>(route: R, params: never): string;
function buildUrl<R extends Route>(route: R, params: PathParams<R>): string;
function buildUrl(route: any, params: any) {
// ...
return route
}
buildUrl('/unknown-route') // expect error
buildUrl('/no-placeholders', {}) // expect error
buildUrl('/no-placeholders')
buildUrl('/path/:p1/:p2') // expect error
buildUrl('/path/:p1/:p2', { foo: '' }) // expect error
buildUrl('/path/:p1/:p2', { p1: 'foo', p2: 'bar' })
问题是 buildUrl
总是需要传递第二个参数,即使给定的路由没有占位符。当 PathParams<R>
returns never
?
时,有没有办法让第二个参数可选?
解决方案
在重载中显式处理没有占位符的路由:
type RouteWithoutParams = '/no-placeholders'
type RouteWithParams = '/path/:p1/:p2'
type Route = RouteWithoutParams | RouteWithParams
function buildUrl<R extends RouteWithoutParams>(route: R): string;
function buildUrl<R extends Route>(route: R, params: PathParams<R>): string;
function buildUrl<R extends string>(route: R, params?: PathParams<R>) {
// ...
return route
}
如果你想处理丢失的参数,你需要明确地将其标记为可选:
type PathParams<Path extends string> =
Path extends `:${infer Param}/${infer Rest}` ? { [k in Param | keyof PathParams<Rest>]: string } :
Path extends `:${infer Param}` ? { [k in Param]: string } :
Path extends `${infer _Prefix}:${infer Rest}` ? { [k in keyof PathParams<`:${Rest}`>]: string } :
never;
type Route = '/no-placeholders' | '/path/:p1/:p2'
function buildUrl<R extends '/no-placeholders'>(route: R): string;
function buildUrl<R extends Route>(route: R, params: PathParams<R>): string;
function buildUrl<R extends string>(route: R, params?: PathParams<R>) {
// ...
return route
}
buildUrl('/unknown-route') // error, route does not exist
buildUrl('/no-placeholders', {}) // error, expected 1 param got 2
buildUrl('/path/:p1/:p2') // error, expected 2 params, got 1
buildUrl('/path/:p1/:p2', { foo: '' }) // error, { foo: string } does not match { p1: string; p2: string }
buildUrl('/no-placeholders') // ok
buildUrl('/path/:p1/:p2', { p1: 'foo', p2: 'bar' }) // ok
您需要为文字 '/no-placeholders'
显式定义重载规则
我想写一个给定路由和一组可选参数的函数 returns url 路由中的占位符替换为给定的参数。
参数的类型应该与路由中的占位符匹配,如果路由没有占位符,则参数应该是可选的。这是预期结果的示例:
type Route = '/no-placeholders' | '/path/:p1/:p2'
buildUrl('/unknown-route') // error, route does not exist
buildUrl('/no-placeholders', {}) // error, expected 1 param got 2
buildUrl('/no-placeholders')
buildUrl('/path/:p1/:p2') // error, expected 2 params, got 1
buildUrl('/path/:p1/:p2', { foo: '' }) // error, { foo: string } does not match { p1: string; p2: string }
buildUrl('/path/:p1/:p2', { p1: 'foo', p2: 'bar' })
所以,我得出的结果如下,重载在这里没有用,但我试图以不同的方式处理 never
的情况:
type PathParams<Path extends string> =
Path extends `:${infer Param}/${infer Rest}` ? { [k in Param | keyof PathParams<Rest>]: string } :
Path extends `:${infer Param}` ? { [k in Param]: string } :
Path extends `${infer _Prefix}:${infer Rest}` ? { [k in keyof PathParams<`:${Rest}`>]: string } :
never;
type Route = '/no-placeholders' | '/path/:p1/:p2'
function buildUrl<R extends Route>(route: R, params: never): string;
function buildUrl<R extends Route>(route: R, params: PathParams<R>): string;
function buildUrl(route: any, params: any) {
// ...
return route
}
buildUrl('/unknown-route') // expect error
buildUrl('/no-placeholders', {}) // expect error
buildUrl('/no-placeholders')
buildUrl('/path/:p1/:p2') // expect error
buildUrl('/path/:p1/:p2', { foo: '' }) // expect error
buildUrl('/path/:p1/:p2', { p1: 'foo', p2: 'bar' })
问题是 buildUrl
总是需要传递第二个参数,即使给定的路由没有占位符。当 PathParams<R>
returns never
?
解决方案
在重载中显式处理没有占位符的路由:
type RouteWithoutParams = '/no-placeholders'
type RouteWithParams = '/path/:p1/:p2'
type Route = RouteWithoutParams | RouteWithParams
function buildUrl<R extends RouteWithoutParams>(route: R): string;
function buildUrl<R extends Route>(route: R, params: PathParams<R>): string;
function buildUrl<R extends string>(route: R, params?: PathParams<R>) {
// ...
return route
}
如果你想处理丢失的参数,你需要明确地将其标记为可选:
type PathParams<Path extends string> =
Path extends `:${infer Param}/${infer Rest}` ? { [k in Param | keyof PathParams<Rest>]: string } :
Path extends `:${infer Param}` ? { [k in Param]: string } :
Path extends `${infer _Prefix}:${infer Rest}` ? { [k in keyof PathParams<`:${Rest}`>]: string } :
never;
type Route = '/no-placeholders' | '/path/:p1/:p2'
function buildUrl<R extends '/no-placeholders'>(route: R): string;
function buildUrl<R extends Route>(route: R, params: PathParams<R>): string;
function buildUrl<R extends string>(route: R, params?: PathParams<R>) {
// ...
return route
}
buildUrl('/unknown-route') // error, route does not exist
buildUrl('/no-placeholders', {}) // error, expected 1 param got 2
buildUrl('/path/:p1/:p2') // error, expected 2 params, got 1
buildUrl('/path/:p1/:p2', { foo: '' }) // error, { foo: string } does not match { p1: string; p2: string }
buildUrl('/no-placeholders') // ok
buildUrl('/path/:p1/:p2', { p1: 'foo', p2: 'bar' }) // ok
您需要为文字 '/no-placeholders'