Typescript Promise-Ablehnungstyp

Lesezeit: 6 Minuten

Wie stelle ich die Art der Ablehnung meines Versprechens ein? Sagen wir, ich tue:

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

Nehmen wir an, ich möchte mit einer Nummer ablehnen. Aber ich kann den Typ nicht einstellen; Ich kann an die weitergeben, was ich will reject hier.

Außerdem möchte ich bei der Verwendung dieses Versprechens einen Kompilierungsfehler haben, wenn ich den Ablehnungsantworttyp falsch verwende.

  • Dieses Problem kann von Interesse sein.

    – CRice

    27. April 2018 um 22:58 Uhr

Wie in erklärt dieses Problem, Promise hat keine unterschiedlichen Typen für erfüllte und abgelehnte Versprechen. reject akzeptiert any Streit das hat keinen Einfluss auf die Art eines Versprechens.

Zur Zeit Promise besser kann man es nicht schreiben. Dies ergibt sich daraus, dass ein Versprechen abgelehnt werden kann throwdrinnen then oder catch (Dies ist ein bevorzugter Weg, um ein vorhandenes Versprechen abzulehnen), und dies kann nicht vom Typisierungssystem gehandhabt werden. Außerdem hat TypeScript auch keine ausnahmespezifischen Typen außer never.

  • Beachten Sie, dass dies mit der Art der abgefangenen Fehler übereinstimmt

    – Aluan Haddad

    27. April 2018 um 23:58 Uhr

Da es in einigen Fällen wie Promise oder Ausnahmeauslösungen keine Möglichkeit gibt, den Fehlertyp festzulegen, können wir mit Fehlern im Rost-ähnlichen Stil arbeiten:

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

  • Ich würde davon abraten, dieses Muster zu verwenden. Das Problem ist, dass Sie jetzt zwei Möglichkeiten der Fehlerbehandlung haben, indem Sie den Fehlertyp des Ergebnisses überprüfen und verwenden .catch. Sie können natürlich in Ihrer gesamten Codebasis davon ausgehen, dass nur das Fehlerergebnis verwendet wird, aber ich versichere Ihnen, dass diese Annahme Sie in jedem ernsthaften Projekt etwas kosten wird.

    – Lodewijk Bogaards

    22. November 2020 um 0:53 Uhr

  • Eigentlich ist es ein Muster aus der funktionalen Programmierung namens Entweder, mit allen daraus resultierenden Konsequenzen

    – KEIII

    22. November 2020 um 10:50 Uhr

  • Das weiß ich, aber das ändert nichts an dem, was ich gesagt habe. Die Annahme ist die Ursache des Problems. Allerdings auch Result mag ein echter Summentyp sein, Promise ist keine Monade, also sind Sie wirklich nicht im FP-Land. Ich bin diesen Weg viel zu oft gegangen und nicht nur mit TypeScript. Um ehrlich zu sein, sollte ich ein bisschen nachsichtig sein, denn bis jemand einen echten monadischen Bifunktor für die asynchrone Programmierung in TypeScript erstellt, werde ich jedes Mal zusammenzucken, wenn ich das Wort sehe Promise. Wenn Gott mir ein bisschen mehr Zeit geben würde, wäre ich dieser Typ.

    – Lodewijk Bogaards

    23. November 2020 um 21:25 Uhr

  • @GabrielC.Troia haha! Ich habe kürzlich Ihre Bibliothek gefunden und sie unserem Frontend-Tech-Lead gezeigt und ihm gesagt: So sollte es gemacht werden 🙂 Wenn Sie jemals einen Job suchen: Sie können jederzeit bei StackState arbeiten!

    – Lodewijk Bogaards

    21. April 2021 um 14:27 Uhr

  • @LodewijkBogaards Wow!! Das hat mir definitiv den Morgen versüßt 🙂 Vielen Dank für Ihre freundlichen Worte, Sir. Ich würde mich sehr freuen, mit Ihnen zusammenzuarbeiten, wenn Sie es jemals bei StackState verwenden.

    – Gabriel C. Troia

    21. April 2021 um 16:27 Uhr

Die Ausnahme ist typisiert Any, da wir den korrekten Typ der Ausnahme zur Entwurfszeit nicht garantieren können und weder TypeScript noch JavaScript die Möglichkeit bieten, Ausnahmetypen zur Laufzeit zu schützen.
Am besten verwenden Sie Typwächter, um sowohl eine Entwurfszeit- als auch eine Laufzeitprüfung in Ihrem Code bereitzustellen.

Quelle

Was @EstusFlask in seiner Antwort erwähnt hat, ist richtig.

Aber Ich möchte einen Schritt in die Nähe eines gehen künstliche Lösung zu
simulieren mit was wir wollen TypeScript Fähigkeiten.

Manchmal verwende ich dieses Muster in meinen 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)
    }
})

Sie können einen Proxy verwenden, um dies explizit zu erzwingen resolve und reject Argumenttypen. Das folgende Beispiel versucht nicht, den Konstruktor eines Versprechens nachzuahmen – weil ich das in der Praxis nicht nützlich fand. Ich wollte eigentlich telefonieren können .resolve(...) und .reject(...) als Funktionen außerhalb des Konstruktors. Auf der Empfängerseite wird das nackte Versprechen verwendet – z. 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 };
}

Das let Anweisungen sehen aus, als wären sie sinnlos – und sie sind zur Laufzeit sinnlos. Aber es stoppt Compilerfehler, die sonst nicht einfach zu beheben wären.

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

Ruft wie gezeigt an .resolve und .reject sind richtig typisiert überprüft.

Oben wird kein Versuch unternommen, eine Typenüberprüfung auf der Empfangsseite zu erzwingen. Ich habe mit dieser Idee herumgestochert und etwas hinzugefügt .then und .catch Mitglieder, aber was sollen sie dann zurückgeben? Wenn sie zurückkehren a Promise dann wird es wieder zu einem normalen Versprechen, also ist es sinnlos. Und es scheint, dass es keine andere Wahl gibt, als das zu tun. Also wird das nackte Versprechen verwendet await, .then und .catch.

1122210cookie-checkTypescript Promise-Ablehnungstyp

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy