【问题标题】:Typescript: Type narrowing on promises打字稿:缩小承诺的类型
【发布时间】:2017-09-19 14:12:15
【问题描述】:

我正在尝试弄清楚如何根据参数对 Promise 使用类型保护。

function request({ logic }: { logic: boolean }) {
    return new Promise((resolve, reject) => {
        if (l)
            resolve("something");
        resolve(1);
    });
}

request({ logic: true }).then(a => {
    a.length
})

在这个例子中,我想确保 typeof 'a' == 'string'。我尝试在request 中编写一些类型保护,但结果丢失了。我不知道这是否只是打字稿的限制,还是我只需要做一些智能类型转换或什么。

这是我实际尝试做的一个玩具示例,即进行异步调用,其结果根据某些参数略有不同。而且我不愿意为了覆盖改变的返回类型而创建另一个函数

【问题讨论】:

    标签: javascript typescript promise


    【解决方案1】:

    Typescript 函数重载救援:

    function request(logic: true): Promise<string>;
    function request(logic: false): Promise<number>;
    function request(logic: boolean) {
        return new Promise((resolve, reject) => {
            if (logic) 
                resolve("something");
            resolve(1);
        });
    }
    
    request(true).then(a => {
        console.log(a.length); //<-- knows that a is a string
    });
    
    request(false).then(a => {
        console.log(a.length); //<-- error: prop 'length' does not exist on number
    });
    

    Typeguard 用于if 语句。

    编辑

    你会感到惊讶! Typescript 也支持基于字段的重载区分!检查以下代码:

    function request(opts: { logic: true }): Promise<string>;
    function request(opts: { logic: false }): Promise<number>;
    function request(opts: { logic: boolean }) {
        return new Promise((resolve, reject) => {
            if (opts.logic) 
                resolve("something");
            resolve(1);
        });
    }
    
    
    request({ logic: true }).then(a => {
        console.log(a.length); //<-- knows that a is a string
    });
    
    request({ logic: false }).then(a => {
        console.log(a.length); //<-- error: prop length cannot be found on type number
    });
    

    编辑

    使用一点通用魔法,您就可以实现所需的行为。这样,从调用者的角度来看,只有 logic 字段很重要。缺点是即使在 request 函数实现中的 opts.logic 也会丢失类型检查。

    function request<T extends { logic: true }>(opts: T): Promise<string>;
    function request<T extends { logic: false }>(opts: T): Promise<number>;
    function request(opts: any) {
        return new Promise((resolve, reject) => {
            if (opts.logic) 
                resolve("something");
            resolve(1);
            console.log(opts.anything);
        });
    }
    
    request({ logic: true, foo: 'bar' }).then(a => {
        console.log(a.length); //<-- knows that a is a string
    });
    
    request({ logic: false, foo: 'baz' }).then(a => {
        console.log(a.length); //<-- error: prop length cannot be found on type number
    });
    

    【讨论】:

    • 这应该可行,尽管我的实际情况有点复杂。我正在使用命名参数。我可以有这样的签名:function request({ logic: true }){ ... }
    • 没关系,我有点转身了。我将只使用稍微定制的 requestconfig 类型:` interface requestConfig { ..whatever } interface stringRequestConfig extends requestConfig { logic: true } function request(config: stringRequestConfig): Promise; `
    • 太棒了!但是,如果我不想指定所有其他配置参数,它如何工作?假设我不只是有逻辑,而是说:url、查询等。这些都是配置的共同点。
    • @PaulSachs 我认为指定所有字段是一个好习惯,它具有更好的完成和类型检查。不过还是可以的,看更新
    • 到目前为止看起来不错,我只需要处理我的设置方法之间有一个中间函数。我想介于两者之间的所有内容也都需要重载定义。
    【解决方案2】:

    正确的重载是下一个(应该加类型):

    function request(logic: boolean): Promise<string>;
    function request(logic: boolean): Promise<number>;
    function request(logic: boolean): Promise<any>;
    function request(logic: boolean) {
        return new Promise((resolve, reject) => {
            if (logic) 
                resolve("something");
            resolve(1);
        });
    }
    
    request(true).then((a) => {
        console.log(a.length); //<-- knows that a is a string
    });
    
    request(false).then((a) => {
        console.log(a.length); //<-- error: prop 'length' does not exist on number
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-16
      • 2019-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-17
      • 1970-01-01
      • 2020-03-10
      相关资源
      最近更新 更多