I have a factory that returns a Query Object with structure like this:
query.ts
class Query<T> {
private queryList: Record<string, string>
async init(url: string, queries: Record<string, string>) {
this.queryList = queries
}
execute(command: string extends keyof this.queryList,
parameter: Record<string, string>
): Promise<T[]> {
}
}
export async function InitializeQuery<T>(
url: string,
queries: Record<string, string>
) {
const queryObject = new Query<T>();
await queryObject.init(url, queries)
return queryObject;
}
And a table definition as this:
table.d.ts
type ItemType = {
id: string,
name: string,
weight: number,
createdBy: string
}
So I execute it like:
index.ts
import { InitializeQuery } from './query.js';
const itemNamedQueries: Record<string, string> = {
"selectById": "SELECT * FROM Table WHERE id = :id",
"selectByName": "SELECT * FROM Table WHERE name = :name",
"findAll": "SELECT * FROM Table"
}
const query = await InitializeQuery<ItemType>(
'mysql://user:pass@localhost/database',
itemNamedQueries);
const result = await query.execute("selectById", {id: 25})
The code is currently works fine, but I found that I made typos here and there regarding itemNamedQuery
names.
So, I want that if I run execute(
, and press ctrl+space, the IDE would recognize that command parameter from execute method are the keys of that namedQueries
and give me a list of possible commands from the index.
How I can achieve that?
UPDATE: I tried making this simple test in VSCode:
const test: Record<string, string> = {
FindAll: "SELECT * FROM TableData",
Find: "SELECT * FROM TableData WHERE id = :id",
} as const
function fn<C extends keyof typeof test>(command: C) {
}
fn()
and it didn't work.
When using namedQueries
constant, you must not loose its precise type.
Currently, you do so by giving it explicit type Record<string, string>
After that, you can use generics to propagate that type across your functions.
const namedQueries = {
"selectById": "SELECT * FROM Table WHERE id = :id",
"selectByName": "SELECT * FROM Table WHERE name = :name",
"findAll": "SELECT * FROM Table"
} as const satisfies Record<string, string>;
export async function InitializeQuery<QUERIES extends Record<string, string>>(url: string, queries: QUERIES) {
const queryObject = new Query<QUERIES>();
await queryObject.init(url, queries)
return queryObject;
}
class Query<QUERIES extends Record<string, string>> {
private queryList: QUERIES = null as any;
async init(url: string, queries: QUERIES) {
this.queryList = queries
}
execute(command: keyof QUERIES, parameter: Record<string, string>) {
}
}
const query = await InitializeQuery('mysql://localhost', namedQueries);
const resultOk = await query.execute("selectById", {id: '25'}) //OK
const resultBad = await query.execute("selectById1", {id: '25'}) //Expected error
BTW it would make sense to also define accepted parameters in namedQueries
and accept only those.