This is a continuation of a question about resolving return types. I'm so close to what I'm looking to do (which is to have a common function to build model instances based on validated data) and it works fine in JS, but I can't figure out the final part for the TS typing.
The key is the return for my Init type - specifically the Builder<T[K]> part. I understand why that results in Builder<(id: number) => Promise<Test>> as the returned type, but I can't for the life of me figure out how to tell TS that in fact it is just Test being returned, not the function that returns a Promise of Test.
The following can be copy / pasted into VSCode (or whatever) to show the issue. The ter1 variable should be an instance of Test, and when compiled to JS it is, but TS doesn't see that.
export interface Builder<T> {
(values: any, db: any): Promise<T>
}
export interface Wrapper {
<T, TId>(
construct: (id: TId, db: any | null) => Promise<T>,
idSrc: string | string[],
errorMsg: string
): Builder<T>
}
let builder: Wrapper = function (construct, idSrc, errorMsg ) {
// A function that can be used to construct the instance
return async function(values: any, db: any | null) {
let id = null;
let inst = await construct(id, db);
if (!inst) {
throw new Error(errorMsg);
}
return inst;
};
}
class Test {
is1 = true;
static async fromId( id: number ) {
var inst = new Test();
// Some async action (e.g. a db read)
return inst;
}
}
class Test2 {
is2 = true;
static async fromId( id: number ) {
var inst = new Test2();
// Some async action (e.g. a db read)
return inst;
}
}
type Config<T extends {}> = {
inputs?: {[index:string]: any},
inst?: T;
};
type Init = <T extends {[key:string]: any}>(
db: any,
config: Config<T>
) => Promise<{[K in keyof T]: Builder<T[K]>}>; // ???
let init: Init = async function ( db, config ) {
let ret: any = {};
if ( config.inst ) {
for (let [key, value] of Object.entries(config.inst)) {
let res = await value( {}, {} );
ret[ key ] = res;
}
}
return ret;
}
async function allan () {
let { ter1, ter2 } = await init( null, {
inst: {
ter1: Test.fromId,
ter2: Test2.fromId
}
} );
console.log( ter1.is1, ter1.is2 ); // should be `true undefined`
// Test `builder` typing
var t1Wrapper = builder( Test.fromId, 'id', 'test');
var t2Wrapper = builder( Test2.fromId, 'id', 'test');
var t1 = await t1Wrapper({}, {});
var t2 = await t2Wrapper({}, {});
t1.is1;
t2.is2;
}
allan();
Thank you!