javascript - Warum verlangt TypeScript, dass optionale Parameter nach obligatorischen Parametern kommen?
Das funktioniert:
public console(message: string | undefined, suppressLevel: boolean) {}
Aber das tut es nicht:
public console(message?: string, suppressLevel: boolean) {}
Wenn man bedenkt, dass ? im Grunde eine Kurzschreibweise für | undefined zu sein scheint (und wenn man in VS Code mit der Maus darüber fährt, wird genau das angezeigt), warum macht TypeScript dann einen Unterschied? Man könnte explizit undefined für einen optionalen Parameter übergeben, und es wäre dasselbe, als würde man ihn am Ende nicht angeben.
Antwort #1
Am wichtigsten ist, dass dies eine JavaScript-Tatsache ist, die nur durch das strikte Typisieren einer Funktionssignatur deutlicher gemacht wird.
Funktionsparameter werden positionsabhängig in der Argumentliste gebunden. Das erste übergebene Argument wird an den ersten Parameter gebunden. Das zweite übergebene Argument wird an den zweiten Parameter gebunden. Und so weiter. Mit den optionalen Parametern am Ende weiß eine JavaScript-Engine, dass die Endparameter auf undefined gebunden werden, wenn die Argumentliste zu kurz ist. Egal wie kurz der Aufruf ist, genauso viele der Endparameter werden auf undefined gesetzt.
Wenn die optionalen Parameter in der Mitte oder am Anfang sein könnten, gäbe es an der Aufrufstelle nicht genügend Informationen, um zu wissen, welche Parameter an welche Argumente gebunden werden sollten.
function fn(a?: number, b: number) { /* ... */ }
fn(42);
Wenn die Signatur von fn gültig wäre, würde fn(42) mehrdeutig sein. Sollte b auf 42 gebunden werden und a undefined bleiben? Oder sollte a auf 42 gebunden werden und b undefined bleiben (und daher ein Typfehler auftreten)? In dieser speziellen Situation möchten Sie vielleicht, dass der TypeScript-Typprüfer ANNIMMT, dass kein Fehler vorliegt, und stattdessen sein Bestes versucht, eine gültige Interpretation des Aufrufs zu finden (diejenige, bei der b 42 erhält), aber das wäre ein aufwendigerer Überladungsauflösungsalgorithmus, und es gäbe immer noch Fälle, die mehrdeutig sind, und die Regeln wären zu kompliziert, um nützlich zu sein (d. h. fn2(a?: number, b: number, c?: number) { /* ... */ } mit fn(42, 3.1415)).
Javascript, indem es positionsabhängige Argumente mit optionalen nachgestellten Argumenten bietet und Typescript uns erlaubt, die erlaubten Signaturüberladungen explizit zu annotieren, erzielt ein gutes Gleichgewicht zwischen Ausdruckskraft, Angemessenheit (Leistung des Typprüfers) und Fehlervermeidung.
Wenn die Überladungsauflösung in dieser Weise flexibler wäre, dann müsste sie zwangsläufig mehr Aufrufe als gültig betrachten, die tatsächlich Fehler sein könnten. Oder sie würde die Last auf den Funktionskörper legen, mit allen Variationen vollständig umzugehen, wie zum Beispiel alle Parameterkombinationen miteinander zu Union-Typen zu machen und diese dann im Code auseinanderzuholen, was das Lesen erschwert.
Antwort #2
? ist nicht die Kurzschreibweise für string | undefined. Es ist ein optionaler Parameter. Das bedeutet, dass Sie entweder den erwarteten Werttyp übergeben können oder nicht.
string | undefined bedeutet hingegen, dass Sie explizit einen Wert übergeben müssen, der entweder ein String oder undefined ist. Der Wert kann undefined sein, aber der Parameter existiert dennoch.
Es wäre paradox, eine Eigenschaft nach einer optionalen Eigenschaft zu übergeben - man könnte sie nicht setzen, da das Setzen bedeuten würde, eine vorherige zu setzen, was bedeutet, dass deren Vorgänger dann nicht optional ist!
