我正在尝试使用typescript在NodeJS上创建一个API
我有以下接口:
export interface ISqlResonse<T> {
success?: string
error?: string
data: Array<T> //Nothing | array of object when are db operations
}
export interface IApiResponse<T> {
status: 'error' | 'success'
message: string
data: Array<T>
}
每个api调用都调用一个函数,该函数调用通用类名DB,该类从数据库中选择/插入/更新/删除数据
例如,更新功能如下所示:
async updateData(input: IUpdateParam) : Promise<ISqlResonse<object>> {
...
...
}
API函数调用DB,如下所示:
async update(req): Promise<IApiResponse<IAccessPointsTableStructure>> {
let data = req.body ;
let updateObj = {
data ,
table: 'accessPoints',
excludeColumns: 'loggedUserId',
additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
validationRules,
where: `id=${data.id}`,
returningData: true
}
let sqlResults = await db.updateData(updateObj) ; // !!!
if(typeof sqlResults.error==="string") {
logger.log('error','Error on updating Access Points!',{sql: db.getQuery(), error: sqlResults.error});
return({status:'error',message: 'Error on updating Access Points!',data: sqlResults.data});
}
logger.log('success', 'Access Points data updated with success!');
return({status: 'error', message: 'Access Points data updated with success!', data: sqlResults.data})
}
我的问题是:如何调用函数db.updateData()并告诉该函数我要从ISqlResponse接收数据,该数组包含对象IAccessPointsTableStructure等对象。
换句话说,我想控制函数的返回类型。我用不同的方法测试了几次。 (将机智替换为db.updateData(...) ...
谢谢你的指教。
最佳答案
您尚未包括IUpdateParam
的定义,但我将假定其table
属性是决定updateData()
返回的事物类型的因素。我评论过“猜猜”的每个地方都只是举例。您应该更改它们以适合您的用例。
您应该能够修改updateData()
的签名,以反映传入的IUpdateParam
类型和返回的Promise<ISqlResponse<{}>>
类型之间的关系。这是使用generics的一种方法(可以使用overloads代替)。首先,声明一个类型,以表示表名和每个表的数据类型之间的映射。例如:
export type TableNameToTypeMapping = {
accessPoints: IAccessPointsTableStructure,
otherThings: IOtherThingsTableStructure, // guess
// ...
}
现在,您可以更改
IUpdateParam
的定义,使其仅接受table
的正确值:export interface IUpdateParam<K extends keyof TableNameToTypeMapping> {
data: any, // guess
table: K,
excludeColumns: string, // guess
additionalColumns: any, // guess
validationRules: any, // guess
where: string // guess
}
因此,
IUpdateParam<'accessPoints'>
对象用于处理accessPoints
表,它与IUpdateParam<'otherThings'>
对象不同。现在,
updateData()
的签名可以更改为:async updateData<K extends keyof TableNameToTypeMapping>(
input: IUpdateParam<K>
): Promise<ISqlResonse<TableNameToTypeMapping[K]>> {
// implement this! The implementation is likely not
// type-safe unless you use runtime type guards
}
这意味着,如果使用类型为
updateData
的参数调用IUpdateParam<'accessPoints'>
,则将返回Promise<ISqlResponse<TableNameToTypeMapping['accessPoints']>>
。但是TableNameToTypeMapping['accessPoints']
只是IAccessPointsTableStructure
,因此您可以根据需要重新获得Promise<ISqlResponse<IAccessPointsTableStructure>>
。请注意,对象文字
updateObj
的table
属性将推断为类型string
,该属性太宽了。为了确保对updateData()
的调用能够按需工作,您将需要断言updateObj.table
属性的文字类型为'accessPoints'
,如下所示:let updateObj = {
data,
table: 'accessPoints' as 'accessPoints', // assertion
excludeColumns: 'loggedUserId',
additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
validationRules,
where: `id=${data.id}`,
returningData: true
}
否则,您需要声明
updateObj
是IUpdateParam<'accessPoints'>
类型,如下所示:// type declaration
let updateObj:IUpdateParam<'accessPoints'> = {
data ,
table: 'accessPoints',
excludeColumns: 'loggedUserId',
additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
validationRules,
where: `id=${data.id}`,
returningData: true
}
两种方法都应该起作用。
希望能有所帮助;祝好运!