繁体   English   中英

Typescript Promise 拒收型

[英]Typescript Promise rejection type

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

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

假设我想拒绝一个数字。 但是我不能设置类型; 我可以将任何我想要的传递给这里的reject

而且,在使用这个promise时,如果我错误地使用拒绝响应类型,我想编译错误。

本期所述Promise没有不同类型的已完成和已拒绝的Promise reject 接受any不影响承诺类型的参数

目前无法更好地键入Promise 这是因为可以通过throw ing inside thencatch来拒绝承诺(这是拒绝现有承诺的更好方法),而这不能通过类型系统处理; 此外, TypeScript 也没有特定于异常的类型,除了never

因为在某些情况下无法设置错误类型,例如 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;
    }
  }
});

异常类型为 any 是因为我们无法在设计时保证异常的正确类型,并且 TypeScript 和 JavaScript 都没有提供在运行时保护异常类型的能力。 您最好的选择是使用类型保护在您的代码中提供设计时和运行时检查。

来源

@EstusFlask 在他的回答中提到的是正确的。

我想更接近一种人工解决方案,模拟我们想要的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)
    }
})

您现在可以使用PromiseRejectionEvent来输入(当然,除了 IE 之外的所有内容):

MDN 文档

微软文档

您可以使用代理来显式强制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 ,那么它就会恢复为一个正常的 Promise,所以它是没有意义的。 似乎别无选择,只能这样做。 所以裸 Promise 用于await.then.catch

这是我尝试输入它的尝试:

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

像平常一样使用它:

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

您的 IDE 应该选择reject类型:

在此处输入图像描述

在此处输入图像描述

暂无
暂无

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

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