Typescript Promise 拒绝类型

问题描述 投票:0回答:8

如何设置拒绝承诺的类型?假设我愿意:

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

假设我想用号码拒绝。但我无法设置类型;我可以将我想要的任何东西传递给这里的

reject

此外,当使用这个承诺时,如果我错误地使用拒绝响应类型,我希望出现编译错误。

javascript typescript promise
8个回答
103
投票

本期中所述,

Promise
对于已履行和已拒绝的承诺没有不同的类型。
reject
接受不影响承诺类型的
any
参数

目前

Promise
无法更好地打字。这是因为 Promise 可以通过在
throw
then
内部进行
catch
来拒绝(这是拒绝现有 Promise 的更好方法),而这不能通过打字系统来处理;此外,除了 never
 之外,
TypeScript 也没有特定于异常的类型。


16
投票

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

6
投票

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

来源


5
投票

这是我尝试输入的内容:

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
的类型:


1
投票

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

1
投票

您可以使用代理显式强制

resolve
reject
参数类型。以下示例不会尝试模仿 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


1
投票

这是我的解决方案:

定义一个扩展原始

PromiseConstructor
Promise
的类型:

interface TypedPromiseConstructor<ResolveType, RejectType> extends PromiseConstructor {
  /**
   * Creates a new Promise.
   * @param executor A callback used to initialize the promise. This callback is passed two arguments:
   * a resolve callback used to resolve the promise with a value or the result of another promise,
   * and a reject callback used to reject the promise with a provided reason or error.
   */
  new <T = ResolveType, R = RejectType>(
    executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: R) => void) => void
  ): TypedPromise<T, R>;
}

interface TypedPromise<ResolveType, RejectType> extends Promise<ResolveType> {
  /**
   * Attaches a callback for only the rejection of the Promise.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of the callback.
   */
  catch<TResult = never>(
    onrejected?: ((reason: RejectType) => TResult | PromiseLike<TResult>) | undefined | null
  ): TypedPromise<ResolveType | TResult>;

  /**
   * Attaches callbacks for the resolution and/or rejection of the Promise.
   * @param onfulfilled The callback to execute when the Promise is resolved.
   * @param onrejected The callback to execute when the Promise is rejected.
   * @returns A Promise for the completion of which ever callback is executed.
   */
  then<TResult1 = ResolveType, TResult2 = never>(
    onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike<TResult1>) | undefined | null,
    onrejected?: ((reason: RejectType) => TResult2 | PromiseLike<TResult2>) | undefined | null
  ): Promise<TResult1 | TResult2>;
}

然后在使用时将

Promise
类型转换为
TypedPromiseConstructor

const promise = new (Promise as TypedPromiseConstructor<number, Error>)((resolve, reject) => {
  if (Date.now() % 2 === 0) {
    reject(new Error(`Worker stopped with exit code: 1`));
  } else {
    resolve(0);
  }
});

0
投票

这是我解决它的方法(类似于@moontai0724):

// typed-promise.d.ts

interface TypedPromise<RESOLVE, REJECT> extends Promise<RESOLVE> {

  then<TResult1 = RESOLVE, TResult2 = never>(
      onfulfilled?: (((value: RESOLVE) => (TResult1 | PromiseLike<TResult1>)) | undefined | null),
      onrejected?: (((reason: (REJECT | any)) => (TResult2 | PromiseLike<TResult2>)) | undefined | null)
  ): TypedPromise<TResult1 | TResult2>;

  catch<TResult = never>(onrejected?: (((reason: (REJECT | any)) => (TResult | PromiseLike<TResult>)) | undefined | null)): TypedPromise<RESOLVE | TResult>;

  finally(onfinally?: ((() => void) | undefined | null)): TypedPromise<RESOLVE | REJECT>;

}
// typed-promise-constructor.d.ts

interface TypedPromiseConstructor<RESOLVE, REJECT> extends PromiseConstructor {

  new<RES = RESOLVE, REJ = REJECT>(
      executor: ((resolve: ((value: (RES | PromiseLike<RES>)) => void), reject: ((reason?: REJ) => void)) => void)
  ): TypedPromise<RES, REJ>;
}

declare var TypedPromise: TypedPromiseConstructor;
© www.soinside.com 2019 - 2024. All rights reserved.