我有一个NextJS/React应用程序。我们维护了一个内部url的大对象,每个url都定义了一个createPath
函数,看起来像这样:
const URLS = {
dashboard: {
createPath: ({ accountNumber }: { accountNumber: string }) => `/account/${accountNumber}`
},
property: {
createPath: ({ accountNumber, propertyId }: { accountNumber: string, propertyId: string }) => `/account/${accountNumber}/property/${propertyId}`
}
}
字符串
所以当我们在页面之间进行链接时,我们会做这样的事情:
<Link
href={URLS.DASHBOARD.createPath({
accountNumber: 'ABC123', // usually account number come out of the route params
})}
>
Go to account
</Link>
型
这很好用,但这意味着整个项目都在使用该URL的结构。实际上,对象中有很多URL,还有一些嵌套,这使得在没有大量查找和替换的情况下很难更改。
受django reverse工具的启发,我希望能够写这样的东西:
getUrl('dashboard', { 'accountNumber': 'ABC123' })
型
这并不难做到,如果写这样的东西:
export const getUrl = (routeName: RouteNames, params: any): string =>
URLS[routeName].createPath(params);
型
但是,如果我想在参数上使用类型安全呢?
我曾经尝试过(TypescriptPlayground)使用重载来做这件事,所以
type RouteNames = 'property' | 'dashboard';
const URLS = {
dashboard: {
createPath: ({ accountNumber }: { accountNumber: string }) => `/account/${accountNumber}`
},
property: {
createPath: ({ accountNumber, propertyId }: { accountNumber: string, propertyId: string }) => `/account/${accountNumber}/property/${propertyId}`
}
}
function getUrl(
routeName: 'dashboard',
params: {
accountNumber: string;
}
): string;
function getUrl(
routeName: 'property',
params: {
accountNumber: string;
propertyId: string;
}
): string;
function getUrl(
routeName: RouteNames,
params: any
): string {
return URLS[routeName].createPath(params);
}
console.log(getUrl('dashboard', {
accountNumber: 'ABC123',
}))
// prints "/account/ABC123"
console.log(getUrl('property', {
accountNumber: 'ABC123',
propertyId: 'DEF456',
}))
// prints "/account/ABC123/property/DEF456"
console.log(getUrl('property', {
accountNumber: 'ABC123',
propertyID: 'DEF456',
}))
// correct TS error: Object literal may only specify known properties, but 'propertyID' does not exist in type '{ accountNumber: string; propertyId: string; }'. Did you mean to write 'propertyId'?
// prints "/account/ABC123/property/undefined"
型
这工作得很好,但这意味着当我创建一个新的url时,我有三个不同的结构要添加-RouteNames
,URLS
* 和 * getUrl。有人有什么建议吗?我已经仔细检查了NextJS docs,看看我是否只是缺少一个库实用程序,无法找到一个,我也尝试过在getUrl
签名中为参数使用联合类型(所有不同的参数集),但是每个createPath
也必须使用联合,安全性被大大削弱。
2条答案
按热度按时间dvtswwa31#
这种方法可能很有前途。
字符串
pdtvr36n2#
你可以使用一个constAssert(
as const
),这个Assert被一个类型所约束,这个类型代表了你期望的URLS
对象的模式(使用satisfies
运算符),来指示编译器使用文字推断来推断对象的类型。(这也提供了防止在你的程序中稍后发生变化的好处。)然后,您可以派生一个类型
RouteName
,它表示URLS
对象的键,以及一个类型实用程序RouteParams
,它将index关联路由的createPath
函数的参数类型(使用Parameters<Type>
实用程序)。这些类型可以用来创建一个通用的
getUrl
函数,这将帮助你达到你的目标:以一种方式派生类型,通过只需要修改初始数据结构来使未来的更改更加容易。下面是一个例子:TSPlayground
字符串