简体   繁体   English

Typescript Promise 拒收型

[英]Typescript Promise rejection type

How do I set the type of the rejection of my promise?如何设置我的promise的拒绝类型? Let's say I do:假设我这样做:

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

Let's say I want to reject with a number.假设我想拒绝一个数字。 But I cannot set the type;但是我不能设置类型; I can pass whatever I want to the reject here.我可以将任何我想要的传递给这里的reject

Moreover, when using this promise, I want to have compiling error if I use the rejection response type incorrectly.而且,在使用这个promise时,如果我错误地使用拒绝响应类型,我想编译错误。

As explained in this issue , Promise doesn't have different types for fulfilled and rejected promises.本期所述Promise没有不同类型的已完成和已拒绝的Promise reject accepts any argument that doesn't affect type of a promise. reject 接受any不影响承诺类型的参数

Currently Promise cannot be typed any better.目前无法更好地键入Promise This results from the fact that a promise can be rejected by throw ing inside then or catch (this is a preferable way to reject existing promise), and this cannot be handled by typing system;这是因为可以通过throw ing inside thencatch来拒绝承诺(这是拒绝现有承诺的更好方法),而这不能通过类型系统处理; also, TypeScript also doesn't have exception-specific types except never .此外, TypeScript 也没有特定于异常的类型,除了never

Cause there is no way to set error type in some cases like Promise, or exception throws, we can work with errors in rust-like style:因为在某些情况下无法设置错误类型,例如 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;
    }
  }
});

The exception is typed any because we cannot guarantee the correct type of the exception at design time, and neither TypeScript nor JavaScript provide the ability to guard exception types at run time.异常类型为 any 是因为我们无法在设计时保证异常的正确类型,并且 TypeScript 和 JavaScript 都没有提供在运行时保护异常类型的能力。 Your best option is to use type guards to provide both a design-time and run-time check in your code.您最好的选择是使用类型保护在您的代码中提供设计时和运行时检查。

source 来源

What @EstusFlask mentioned in his answer is correct. @EstusFlask 在他的回答中提到的是正确的。

But I want go one step near to an artificial solution to simulate what we want with TypeScript capabilities.我想更接近一种人工解决方案,模拟我们想要的TypeScript功能。

Sometimes I use this pattern in my codes😉:有时我在我的代码中使用这种模式😉:

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)
    }
})

You can now use PromiseRejectionEvent to type this (in everything except IE, of course):您现在可以使用PromiseRejectionEvent来输入(当然,除了 IE 之外的所有内容):

MDN Documentation MDN 文档

Microsoft Documentation 微软文档

You can use a proxy to explicitly force resolve and reject argument types.您可以使用代理来显式强制resolvereject参数类型。 The following example does not try to mimic the constructor of a promise - because I didn't find that useful in practice.下面的例子并没有试图模仿 Promise 的构造函数——因为我发现在实践中没有用。 I actually wanted to be able to call .resolve(...) and .reject(...) as functions outside of the constructor.我实际上希望能够调用.resolve(...).reject(...)作为构造函数之外的函数。 On the receiving side the naked promise is used - eg, await p.promise.then(...).catch(...) .在接收方使用裸承诺 - 例如, 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 };
}

The let statements look as if they are pointless - and they are pointless at runtime. let语句看起来好像毫无意义——而且它们在运行时毫无意义。 But it stops compiler errors that were not easy to resolve otherwise.但它阻止了不容易解决的编译器错误。

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

As shown, calls to .resolve and .reject are properly typed checked.如图所示,对.resolve.reject的调用进行了正确类型检查。

No attempt is made in the above to force type checking on the receiving side.上面没有尝试在接收端强制进行类型检查。 I did poke around with that idea, adding on .then and .catch members, but then what should they return?我确实提出了这个想法,添加了.then.catch成员,但是他们应该返回什么? If they return a Promise then it goes back to being a normal promise, so it is pointless.如果他们返回一个Promise ,那么它就会恢复为一个正常的 Promise,所以它是没有意义的。 And it seems there is no choice but to do that.似乎别无选择,只能这样做。 So the naked promise is used for await , .then and .catch .所以裸 Promise 用于await.then.catch

Here's my attempt at typing it:这是我尝试输入它的尝试:

export class ErrPromise<TSuccess, TError> extends Promise<TSuccess> {
    constructor(executor: (resolve: (value: TSuccess | PromiseLike<TSuccess>) => void, reject: (reason: TError) => void) => void) {
        super(executor);
        // Object.setPrototypeOf(this, new.target.prototype);  // restore prototype chain
    }
}

export interface ErrPromise<TSuccess, TError = unknown> {
    then<TResult1 = TSuccess, TResult2 = never>(onfulfilled?: ((value: TSuccess) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: TError) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

    catch<TResult = never>(onrejected?: ((reason: TError) => TResult | PromiseLike<TResult>) | undefined | null): Promise<TSuccess | TResult>;
}

Use it like normal:像平常一样使用它:

return new ErrPromise<T,ExecError>((resolve, reject) => { ... })

Your IDE should pick up the type of reject :您的 IDE 应该选择reject类型:

在此处输入图像描述

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM