簡體   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