解决TypeScript类型的承诺

时间:2018-11-07 12:07:34

标签: typescript

这是a question about resolving return types的延续。我 so 接近我要去做的事情(它具有一个通用功能,可以基于经过验证的数据来构建模型实例),并且在JS中可以正常工作,但是我无法确定列出TS输入的最后一部分。

关键是我的Init类型的返回值-特别是Builder<T[K]>部分。我知道为什么会导致Builder<(id: number) => Promise<Test>>作为返回类型,但是我一生无法弄清楚如何告诉TS实际上只是返回Test而不是返回的函数返回Test的承诺。

可以将以下内容复制/粘贴到VSCode(或其他任何内容)中以显示问题。 ter1变量应该是Test的实例,并且在编译为JS时是,但是TS没有看到。

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();

谢谢!

2 个答案:

答案 0 :(得分:2)

看起来您想要的类型如下:

type Unpromise<T extends Promise<any>> = T extends Promise<infer U> ? U : never;

type Init = <T extends { [key: string]: (...args: any[]) => Promise<any> }>(
  db: any,
  config: Config<T>
) => Promise<{ [K in keyof T]: Unpromise<ReturnType<T[K]>> }>; 

但是我还没有花时间浏览所有代码,以了解它是否有意义或者您是否正确实现了它(这真的是minimum example吗?),所以请您原谅我陷入其他问题。

说明:您调用init()的方式意味着T应该是一个对象,其属性是返回您所关心的承诺的函数。 (我不确定这些函数的参数类型和数量是否重要)。因此,对于T中的每个属性,您要提取其返回类型,然后提取promise类型。

希望有帮助。祝你好运!

答案 1 :(得分:0)

单位类型是一个返回Promise<{[K in keyof T]: Builder<T[K]>}>的函数。 相反,它应该只返回一个Promise<{[K in keyof T]: T[K]}>;

也在您调用的init函数中 let res = await value( {}, {} ); 但是值是Test的fromId函数,它仅接受一个数字参数。