【问题标题】:Typescript Promise rejection typeTypescript Promise 拒绝类型
【发布时间】:2018-10-08 19:31:34
【问题描述】:

如何设置拒绝我的承诺的类型?假设我这样做:

const start = (): Promise<string> => {
   return new Promise((resolve, reject) => {
      if (someCondition) {
         resolve('correct!');
      } else {
         reject(-1);
      }
   });
}

假设我想用一个数字拒绝。但我无法设置类型;我可以在这里将任何我想要的东西传递给reject

此外,在使用这个promise时,如果我错误地使用了拒绝响应类型,我希望有编译错误。

【问题讨论】:

标签: javascript typescript promise


【解决方案1】:

您可以使用代理来显式强制使用 resolvereject 参数类型。下面的例子并没有试图模仿 Promise 的构造函数——因为在实践中我没有发现它有用。我实际上希望能够将.resolve(...).reject(...) 作为构造函数之外的函数调用。在接收方使用裸承诺 - 例如,await p.promise.then(...).catch(...)

export type Promolve<ResT=void,RejT=Error> = {
  promise: Promise<ResT>;
  resolve: (value:ResT|PromiseLike<ResT>) => void;
  reject:(value:RejT) =>void
};

export function makePromolve<ResT=void,RejT=Error>(): Promolve<ResT,RejT> {
  let resolve: (value:ResT| PromiseLike<ResT>)=>void = (value:ResT| PromiseLike<ResT>)=>{}
  let reject: (value:RejT)=>void = (value:RejT)=>{}
  const promise = new Promise<ResT>((res,rej) => {
    resolve = res;
    reject = rej;
  });
  return { promise, resolve, reject };
}

let 语句看起来好像毫无意义——它们在运行时毫无意义。但它会阻止那些不容易解决的编译器错误。

(async()=>{
  const p = makePromolve<number>();
  //p.resolve("0") // compiler error
  p.resolve(0);
  // p.reject(1) // compiler error 
  p.reject(new Error('oops')); 

  // no attempt made to type the receiving end 
  // just use the named promise
  const r = await p.promise.catch(e=>e); 
})()

如图所示,对 .resolve.reject 的调用已正确键入检查。

上面没有尝试在接收端强制进行类型检查。 我确实提出了这个想法,添加了.then.catch 成员,但是他们应该返回什么呢?如果他们返回一个Promise,那么它又回到了一个正常的承诺,所以这是没有意义的。似乎别无选择,只能这样做。所以裸承诺用于await.then.catch

【讨论】:

    【解决方案2】:

    由于在某些情况下无法设置错误类型,例如 Promise 或异常抛出,我们可以处理类似 rust 风格的错误:

    // Result<T, E> is the type used for returning and propagating errors.
    // It is an sum type with the variants,
    // Ok<T>, representing success and containing a value, and 
    // Err<E>, representing error and containing an error value.
    export type Ok<T> = { _tag: "Ok"; ok: T };
    export type Err<E> = { _tag: "Err"; err: E };
    export type Result<T, E> = Ok<T> | Err<E>;
    export const Result = Object.freeze({
      Ok: <T, E>(ok: T): Result<T, E> => ({ _tag: "Ok", ok }),
      Err: <T, E>(err: E): Result<T, E> => ({ _tag: "Err", err }),
    });
    
    const start = (): Promise<Result<string, number>> => {
      return new Promise((resolve) => {
        resolve(someCondition ? Result.Ok("correct!") : Result.Err(-1));
      });
    };
    
    start().then((r) => {
      switch (r._tag) {
        case "Ok": {
          console.log(`Ok { ${r.ok} }`);
          break;
        }
        case "Err": {
          console.log(`Err { ${r.err} }`);
          break;
        }
      }
    });
    

    【讨论】:

    • 我建议不要使用这种模式。问题是您现在有两种错误处理方式,一种是检查结果的错误类型,另一种是使用.catch。你当然可以假设你的整个代码库只使用错误结果,但我向你保证,在任何规模很大的项目中,这种假设都会让你付出代价。
    • 实际上它是函数式编程的模式,称为 Either,以及随之而来的所有后果
    • 我知道,但这并不能改变我所说的。假设是导致问题的原因。此外,虽然Result 可能是一个真正的 sum 类型,但 Promise 不是一个 monad,所以你真的不在 FP 领域。我已经走这条路太多次了,不仅是使用 TypeScript。老实说,我应该有点宽容,因为在有人为 TypeScript 中的异步编程创建真正的一元双函子之前,我每次看到Promise 这个词都会感到畏缩。如果上帝给我多一点时间,我会成为那个人。
    • @GabrielC.Troia 哈哈!我最近找到了你的库,并把它展示给我们的前端技术负责人并告诉他:应该这样做:) 如果你碰巧找工作:欢迎随时在 StackState 工作!
    • @LodewijkBogaards 哇!!这绝对是我的早晨 :) 非常感谢您的客气话,先生。如果您最终在 StackState 使用它,我将非常乐意与你们合作。
    【解决方案3】:

    @EstusFlask 在his answer 中提到的是正确的。

    但是我想更接近人工解决方案 使用TypeScript 功能模拟我们想要的。

     

    有时我在我的代码中使用这种模式?:

    interface IMyEx{
       errorId:number;
    }
    
    class MyEx implements IMyEx{
       errorId:number;
       constructor(errorId:number) {
          this.errorId = errorId;
       }
    }
    // -------------------------------------------------------
    var prom = new Promise(function(resolve, reject) {
         try {
             if(..........)
                resolve('Huuuraaa');         
             else
                reject(new MyEx(100));
         }
         catch (error) {
                reject(new MyEx(101));
         }
    });
    
    // -------------------------------------------------------
    prom()
    .then(success => {
        try {
            }
        catch (error) {
            throw new MyEx(102);
        }
    })
    .catch(reason=>{
        const myEx = reason as IMyEx;
        if (myEx && myEx.errorId) {
           console.log('known error', myEx)
        }else{
           console.log('unknown error', reason)
        }
    })
    

    【讨论】:

      【解决方案4】:

      异常键入 any 因为我们不能保证正确 设计时异常的类型,既不是 TypeScript 也不是 JavaScript 提供了在运行时保护异常类型的能力。 最好的选择是使用类型保护在代码中提供设计时和运行时检查。

      source

      【讨论】:

        【解决方案5】:

        正如this issue 中所述,Promise 没有不同类型的已履行和已拒绝承诺。 reject accepts any argument 不会影响承诺的类型。

        目前无法更好地键入Promise。这是因为可以通过throwing 在thencatch 中拒绝一个promise(这是拒绝现有promise 的更好方式),而这不能通过打字系统处理;还有,TypeScript also doesn't have exception-specific types except never

        【讨论】:

        • 注意这与捕获到的错误类型一致
        猜你喜欢
        • 2021-12-10
        • 2018-01-21
        • 2017-05-27
        • 2017-03-14
        • 1970-01-01
        • 2021-10-17
        • 1970-01-01
        相关资源
        最近更新 更多