Achtergrond van de vraag:
In sommige gevallen is het nodig dat een functie verschillende dataverstructuren kan retourneren - bijvoorbeeld een array van vaste lengte (tuple) of verschillende resultaatsvarianten (union type). Deze aanpak wordt veel gebruikt in dynamisch JavaScript voor patronen zoals “resultaat of fout” (bijvoorbeeld, [err, value]), terwijl TypeScript duidelijke type-informatie vereist, zodat de daaropvolgende code de structuur van het resultaat correct kan begrijpen.
Probleem:
Zonder expliciete toewijzing van het retourtype, leidt TypeScript het resultaat naar een te ‘brede’ (d.w.z. niet nauwkeurige) vorm - een array of union, wat de ontwikkelaar berooft van de voordelen van autocompletion en typeveiligheid. Bij destructurering van het resultaat van de functie is er een risico op verlies van de exacte beschrijving van de types van de elementen.
Oplossing:
Je moet gebruik maken van conditional types en overload-functies, en voor tuples expliciet het type van de resulterende array definiëren en as const gebruiken (als we een constante tuple retourneren). Voor union-resultaten kun je generics combineren met type narrowing, zodat het buiten de code mogelijk is om het type van het geretourneerde resultaat correct te onderscheiden.
Codevoorbeeld:
type Result<T> = [null, T] | [Error, null]; function parseNumber(str: string): Result<number> { const n = Number(str); return isNaN(n) ? [new Error('Ongeldig nummer'), null] : [null, n]; } const [err, value] = parseNumber('123'); if (err) console.error(err.message); else console.log(value!.toFixed(2)); // Met tuple en as const voor auto-vrije structuren: function getStatus(flag: boolean): [string, number] | string { return flag ? ['ok', 200] as const : 'fout'; } const r = getStatus(true); if (Array.isArray(r)) { // r: readonly [string, number] }
Belangrijke kenmerken:
Zal het type van het array-element na destructurering van de union van de geretourneerde tuple altijd specifiek zijn?
Nee, als de functie union tuples retourneert, zullen de elementen na destructurering een union type van alle mogelijke variaties voorstellen.
type R = [number, null] | [null, string]; const [a, b]: R = [1, null]; // a: number | null // b: null | string
Kan je met as const het type van de tuple volledig “vastleggen” zonder aanvullende typebeschrijvingen van de functie?
Nee, as const fixeert de waarden, maar als de functie is gedeclareerd zonder retourtype, kan TypeScript het type breder afleiden dan nodig is. Het is beter om het retourtype expliciet aan te geven.
function foo() { return [1, 'ok'] as const; } // foo(): readonly [1, "ok"]
Bieden overloads met verschillende geretourneerde types gegarandeerde typebepaling van het resultaat na destructurering?
Overloads helpen de compiler, maar als de invoerparameter onbekend is, zal het resultaat een union zijn van alle opties. Voor een nauwkeurige typebepaling moet je type guards gebruiken in de resultaatverwerker.
function bar(x: number): string; function bar(x: boolean): number; function bar(x: any): any { return typeof x === 'number' ? 'str' : 123; } const r = bar(Math.random() > 0.5 ? true : 1); // r: number | string
De functie retourneert of een array of een fout, maar beschrijft dit niet expliciet in de type handtekening. In de aanroepende code wordt verwacht dat het resultaat een nummer is, en de toegang tot result[1].toFixed(2) gooit een fout tijdens runtime.
Voordelen:
Nadelen:
De functie retourneert een strikt getypeerde tuple met het type Result<T>, de verwerking van het resultaat is gebaseerd op destructurering en expliciete runtime-controle van het eerste element (error/null). De compiler garandeert dat je alleen toegang hebt tot het resultaat na een succesvolle type narrowing.
Voordelen:
Nadelen: