Reputation: 141
I am trying to create an API on NodeJS with typescript
I have the following interfaces :
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>
}
Each api call call a function that call an generic class name DB that select/insert/update/delate data from an database For example the update function look like :
async updateData(input: IUpdateParam) : Promise<ISqlResonse<object>> {
...
...
}
API function call DB and look like :
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})
}
My question is : how can I call the function db.updateData() and tell this function that I want to receive in data from ISqlResponse an array with objects like interface IAccessPointsTableStructure.
With other words i want to control the returning type of function. I teste several times with different approaches . (Replace wit in db.updateData(...) <..>... Thank you in advice.
Upvotes: 2
Views: 16091
Reputation: 327994
You haven't included the definition of IUpdateParam
, but I will assume that its table
property is what decides the type of thing updateData()
returns. Everywhere I've commented "guess" is just for example; you should change them to fit your use cases.
You should be able to modify the signature for the updateData()
to reflect the relationship between the type of IUpdateParam
passed in and the type of Promise<ISqlResponse<{}>>
returned. Here's one way to do it, using generics (you could use overloads instead). First, declare a type to represent the mapping between the table names and the data type for each table. For example:
export type TableNameToTypeMapping = {
accessPoints: IAccessPointsTableStructure,
otherThings: IOtherThingsTableStructure, // guess
// ...
}
Now, you can change the definition of IUpdateParam
so that it only accepts the right values for 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
}
So an IUpdateParam<'accessPoints'>
object is meant to deal with the accessPoints
table, and it is different from an IUpdateParam<'otherThings'>
object.
Now the signature for updateData()
can be changed to:
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
}
This means if you call updateData
with a parameter of type IUpdateParam<'accessPoints'>
, you will get back a Promise<ISqlResponse<TableNameToTypeMapping['accessPoints']>>
. But TableNameToTypeMapping['accessPoints']
is just IAccessPointsTableStructure
, so you are getting back a Promise<ISqlResponse<IAccessPointsTableStructure>>
as desired.
Note that the object literal updateObj
will have its table
property inferred as type string
, which is too wide. To make sure the call to updateData()
works as desired, you will either need to assert that the updateObj.table
property is of literal type 'accessPoints'
, like this:
let updateObj = {
data,
table: 'accessPoints' as 'accessPoints', // assertion
excludeColumns: 'loggedUserId',
additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
validationRules,
where: `id=${data.id}`,
returningData: true
}
or you will need to declare that updateObj
is of type IUpdateParam<'accessPoints'>
, like this:
// type declaration
let updateObj:IUpdateParam<'accessPoints'> = {
data ,
table: 'accessPoints',
excludeColumns: 'loggedUserId',
additionalColumns: { modifiedBy: '1', modifiedAt: crtDate },
validationRules,
where: `id=${data.id}`,
returningData: true
}
Either way should work.
Hope that helps; good luck!
Upvotes: 5