Typescript Promise 拒绝类型

Posted

技术标签:

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

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

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

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

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

【问题讨论】:

This issue 可能感兴趣。 【参考方案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

【讨论】:

注意这与捕获到的错误类型一致

以上是关于Typescript Promise 拒绝类型的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript 泛型:如果 Promise 拒绝和解析类型不同怎么办?

typescript async函数必需返回promise么

带有 Promise 的 typescript 响应类型

使用 Promise 的 TypeScript 异步类型保护

什么 AsyncGenerator TypeScript 类型会产生 Promise?

当我从等待移动到 Promise.all 时,TypeScript 函数返回类型错误