ProgrammatieFrontend ontwikkelaar

Hoe implementeer en typeer je een functie die een union type of tuple retourneert afhankelijk van de parameters? Hoe beschrijf je de geretourneerde waarden correct, zodat je geen strikte type-informatie verliest bij verdere bewerkingen en het gebruik van destructurering?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

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:

  • Gebruik overloads voor functies die verschillende types retourneren afhankelijk van de parameters.
  • Voor tuples gebruik altijd [T, U] of as const, om de exacte lengte en type van de elementen na destructurering te behouden.
  • Union types van geretourneerde waarden vereisen type guards voor een nauwkeurige typebepaling tijdens verdere verwerking.

Vragen met een valstrik.

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

Typefouten en anti-patronen

  • Het beschrijven van de functie als zijnde die any[] of (T | U)[] retourneert, wat leidt tot het “vervagen” van de specificiteit van tuples.
  • Pogingen om destructurering te gebruiken zonder type narrowing en validiteitscontroles van de types van de elementen.
  • Het ontbreken van een directe beschrijving van de structuur van het geretourneerde resultaat - bemoeilijkt het onderhoud en autocompletion.

Voorbeeld uit het leven

Negatieve case

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:

  • Eenvoudig te schrijven code zonder types.

Nadelen:

  • Fout alleen tijdens runtime.
  • Verlies van strikte type-informatie, meer bugs.

Positieve case

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:

  • Voorspelbaarheid en duidelijkheid van de code.
  • Automatische autocompletion voor tuples.

Nadelen:

  • De noodzaak om types handmatig te beschrijven.
  • Verhoogt het aantal type guards en controles.