我正在尝试使用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>>



请注意,对象文字updateObjtable属性将推断为类型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
}


否则,您需要声明updateObjIUpdateParam<'accessPoints'>类型,如下所示:

// type declaration
let updateObj:IUpdateParam<'accessPoints'> = {
    data ,
    table: 'accessPoints',
    excludeColumns: 'loggedUserId',
    additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
    validationRules,
    where: `id=${data.id}`,
    returningData: true
}


两种方法都应该起作用。



希望能有所帮助;祝好运!

08-06 10:15