I have a DatabaseEngine interface, which takes one type parameter: ResultType. This type parameter is the interface of a query result, as specified by the specific database driver; for MSSQL, it would be the IResult interface: DatabaseEngine<IResult>. This type is not the rowset received from a query, but is the "wrapper" that includes not only the rowset, but also various metadata (including fields, affected rows, executed SQL, etc).
The IResult interface from the MSSQL library takes one type parameter, which is the interface defining rows of data being returned. This is a general trend for most database libraries I've used.
The DatabaseEngine interface has a toDataFrame property, which is a function that performs a manipulation (the details of which are arbitrary for this question) on data that has been retrieved from the database. This function has a type parameter T, which encompasses the rows of objects coming from the database; it's one and only parameter should be the result set that has come from the database driver (eg. IResult). However, IResult was encompassed earlier by a type parameter on it's own; so it would need to be ResultType<T>, but this does not work.
TL;DR: I want a type that would be as follows:
interface DatabaseEngine<ResultType> {
...
toDataFrame<T>(result: ResultType<T>): DataFrame<T>
}
// where T is the object type of the rowset from the database, and ResultType is the library-specific wrapper for a query result
A desired usage example of such would be the following:
import mssql from 'mssql'
const msSqlDatabaseEngine: DatabaseEngine<IResult> = {
...
toDataFrame<T>: (result) => {
... // do something with result and return a dataframe
// due to typings, result is inferred as IResult<T>
}
}
const queryResult = mssql.connect({ ... }).query('SELECT * from Employees') // returns an object of type IResult<Employee> (assuming Employee is a type defined elsewhere, the details of which aren't relevant)
const df = msSqlDatabaseEngine.toDataFrame(queryResult) // queryResult can be used directly, since the compiler will infer that the inner generic T is Employee in this function call
I understand that this is a feature request that is still open. The various other SO questions I've read don't quite answer this need I have, in that they don't try to combine type parameters from different places with one as a generic. Is there a potential workaround for what I'm trying to achieve?
I tried the above interface as is, but gave errors saying that "ResultType is not generic", which of course it isn't. I tried to make ResultType generic, but this is not recognised by TypeScript, since it is part of a generic. I'm looking for either a set of utility types that can achieve this, or a workaround that would achieve the same or similar result.
Assuming you can infer T from IResult<T> | IOtherResult<T> | ...,
playground
import mssql from 'mssql'
type MyDbResult<T> = { db: 'my-db', value: T }
type ResultType<T> =
| mssql.IResult<T>
| MyDbResult<T>
type UnwrapResultType<T> =
| T extends ResultType<infer V> ? V : never
// | T extends mssql.IResult<infer V> ? V
// : T extends MyDbResult<infer V> ? V
// : never;
type DataFrame<T> = T[][]
interface DatabaseEngine<BaseResultType extends ResultType<any>> {
// ^ force result to be limited to ThisDBResult<any>
toDataFrame<T extends BaseResultType>(result: T): DataFrame<UnwrapResultType<T>>
}
const msSqlDatabaseEngine: DatabaseEngine<mssql.IResult<any>> = {
toDataFrame(
result /* : result: T extends mssql.IResult<any> */
) /* : DataFrame<UnwrapResultType<T>> */ {
return [[]]
}
}
const mySqlDatabaseEngine: DatabaseEngine<MyDbResult<any>> = {
toDataFrame(
result /* T extends MyDbResult<any> */
) /* DataFrame<UnwrapResultType<T>> */ {
return [[]]
}
}
const con = await mssql.connect('')
const queryResult = await con.query<{ employee: true }>('SELECT * from Employees')
// ^?
// const queryResult: mssql.IResult<{ employee: true; }>
const df = msSqlDatabaseEngine.toDataFrame(queryResult)
// ^?
// const df: DataFrame<{ employee: true; }>
const myQueryResult: MyDbResult<number> = { db: 'my-db', value: 123 }
const mf = mySqlDatabaseEngine.toDataFrame(myQueryResult)
// ^?
// const mf: DataFrame<number>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With