Rolf B: try/catch - warum wird throw new Error() nicht ausgeführt und ist das die richtige Art, zurückzugeben was genau falsch ist?

Beitrag lesen

problematische Seite

Hallo ebody,

ein try-Block, der nichts weiter tut als etwas zu prüfen und im Fehlerfall eine Exception zu werfen, gefolgt von einem catch-Block, der diese Exception gleich wieder wegschnappt, ist Unfug. So ist das nicht gedacht.

Deine check-Funktion sollte überhaupt kein catch enthalten, sondern nur prüfen und bei Fehlern die Exception werfen.

objParameters ist ein komisches Viech - du verwendest es offenbar aus einem Elternkontext heraus (denn es gibt kein let/const und keine Initialisierung dazu), und dann gibst Du es zurück. Also - entweder ist es lokal und Du gibst es zurück, oder es ist aus einem äußeren Kontext und Du veränderst es direkt. Beides geht, beides kann sinnvoll sein.

Deine check-Funktion sollte auch nicht beide Parameter prüfen. Wenn Du das tust, schreibst Du für jede Methode eine eigene Checkfunktion. Du solltest das eher so designen:

class Foo {
   function someMethod(name, age) {
      validateString("Foo:someMethod:name", name);
      validateInt("Foo:someMethod:age", age);

      // normale Verarbeitung
   }
}

function validateString(name, value) {
   if (typeof value != 'string')
      throw new TypeError(name + ": Ungültiger Typ, String erwartet, " + (typeof value ) + " erhalten");
}

try {
   let f = new Foo();
   f.someMethod("Rolf", "zu alt");
}
catch (err) {
   if (err instanceOf TypeError) {
      // Fehler protokollieren
   }
}

Deine Methode validiert und schmeißt die Exception. Fangen sollte sie sie nicht, das macht der Aufrufer. Der try-Block darf auch gerne mehr tun; den TypeError solltest Du nur werfen, wenn eine Fortsetzung des Codes keinen Sinn ergibt. WENN eine sinnvolle Fortsetzung möglich ist, solltest Du keinen Error werfen.

Du kannst auch eine Klasse von TypeError ableiten (glaube ich) und eigene Propertys hinzufügen, z.B. um die von mir im "name" Parameter angedeutete Fehlerstelle in einem separaten Property der Exception zu speichern.

Eine vollständige Analyse aller Fehlerstellen bekommst Du so natürlich nicht. Die erste Exception bricht das ab. Wenn Du mehrere Fehler sammeln willst, brauchst Du eine Aggregierung. Das könnte man so machen:

   function someMethod(name, age) {
      throwErrors("Foo:someMethod(name,age)",
         validateString("name", name);
         validateInt("age", age);
      );

      // normale Verarbeitung
   }

function validateString(name, value) {
   if (typeof value == 'string')
      return null;
   return name + ": Ungültiger Typ, String erwartet, " + (typeof value ) + " erhalten");
}


function throwErrors(method, ...validationMessages) {
   allMessages = validationMessages.filter(msg => !!msg);
   if (allMessages.length > 0) {
      // die gesammelten Messages sinnvoll aufbereiten
      throw new TypeError(...)
   }
}

D.h. die Validierungsmethoden geben null zurück, wenn alles ok ist, und sonst einen Error. Und throwOnError bekommt den Methodennamen sowie die Ergebnisse (per ... Rest-Parameter als Array). Im ersten Schritt schmeißt sie die leeren Ergebnisse weg (!! ist eine doppelte Verneinung, d.h. aus null wird false und der Rest wird true. Du könntest auch ausführlicher und lesbarer msg => msg != null && msg != "" schreiben). Ist dann noch was übrig, baust Du eine Sammelmeldung zusammen und wirfst sie in einem TypeError.

Dort, wo validiert wird, steht nur das nötigste. Prüfung der Parameter und Integration der Meldungen erfolgt in der Helper-Funktion, Abfangen der Exception beim Aufrufer. Man muss nicht alles wegabstrahieren.

Man kann natürlich brutal mit JavaDoc-Kommentaren als Pseudo-Attribute herangehen, und in someMethod.toString() herumparsen (was Dir den Quellcode liefert). Aber ich würde mir damit nicht die Programmiererseele beflecken wollen.

Wie schon mal gesagt: Wenn dir Typprüfungen wichtig sind, entwickle mit TypeScript.

Rolf

--
sumpsi - posui - obstruxi