ProgrammatieFrontend/Backend ontwikkelaar

Hoe werkt de optie Strict Function Types in TypeScript? Hoe beïnvloedt het de typecontroles van functies met covariantie en contravariantie, en in welke gevallen leidt een mismatch in de handtekeningen tot een compilatiefout?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

Achtergrond

Standaard staat TypeScript enige "losheid" toe bij het afstemmen van type-signaturen van functies, waarbij omkeerbare en niet-omkeerbare functies als compatibel worden beschouwd. Met de komst van TypeScript 2.6 is de optie strictFunctionTypes geïntroduceerd, die een strikte typecontrole voor functies garandeert en veel klassen van fouten voorkomt, vooral in grote codebases.

Probleem

Zonder strikte controle kan het voorkomen dat een handler-functie of callback meer of specifieker typeparameters accepteert, wat onopgemerkt kan blijven voor de ontwikkelaar. Dit leidt tot runtime-fouten gerelateerd aan covariantie van geretourneerde types en contravariantie van argumenten.

Oplossing

De optie strictFunctionTypes introduceert strikte contravariantie voor de typeparameters van functies. Nu zijn functies alleen compatibel als de typeparameter van de bron een supertype van de doelparameter is, en niet andersom.

Voorbeeldcode:

type Animal = { name: string }; type Cat = { name: string; meow: () => void }; let animalHandler: (a: Animal) => void; let catHandler: (c: Cat) => void; animalHandler = catHandler; // Fout bij strictFunctionTypes: argument is te specifiek catHandler = animalHandler; // Toegestaan, Cat is subtype van Animal

Belangrijke kenmerken:

  • De argumenten van functies worden gecontroleerd op compatibiliteit van "supertypes" (contravariantie)
  • De geretourneerde waarden worden gecontroleerd op covariantie (subtypes zijn toegestaan)
  • Overtredingen van handtekeningen leiden tot compilatiefouten

Vragen met een snufje bedrog.

Kon je eerder een handler met een specifieker type parameter toewijzen, voordat strictFunctionTypes verscheen?

Ja, vóór het inschakelen van strictFunctionTypes stond TypeScript het toe om specifieker functies toe te wijzen in plaats van algemene, wat leidde tot runtime-problemen:

enum E { A, B } const f: (e: E) => void = (e: E.A) => {} // Zonder strictFunctionTypes: toegestaan

Hoe beïnvloedt strictFunctionTypes callbacks met optionele parameters?

Als de parameters van de callbackfunctie bepaalde parameters optioneel maken, laat strikte controle niet toe dat een functie met minder verplichte parameters wordt gebruikt op een plaats waar een functie met meer parameters wordt verwacht. Dit voorkomt situaties waarin de callback niet de benodigde gegevens ontvangt.

Zullen er compatibiliteitsproblemen zijn bij het inschakelen van strictFunctionTypes in oude projecten?

Ja, er is een risico op nieuwe compilatiefouten, omdat veel functies en handlers mogelijk aan elkaar zijn toegewezen met een schending van de contravariantie. Dit komt vaak voor bij callbacks of bij het gebruik van API's van externe bibliotheken zonder strikte typisering.

Typefouten en anti-patronen

  • Gebruik van verouderde typisering voor callbacks zonder strikte parametercontrole
  • Poging om een functie met een specifieker/nauwkeuriger type parameter toe te wijzen aan een algemenere handler
  • Negeer fouten bij het activeren van strictFunctionTypes (de optie uitschakelen in plaats van types te corrigeren)

Voorbeeld uit de praktijk

Negatieve case

In een groot project accepteren event-handlers specifieker types (MouseEvent in plaats van algemeen Event). Dit wordt pas ontdekt bij het inschakelen van de strikte optie, wat leidt tot fouten bij runtime met verschillende eventbronnen.

Voordelen:

  • Snellere prototyping

Nadelen:

  • Runtime-fouten bij type mismatch van evenementen
  • Moeilijke debugging na uitbreiding van de code

Positieve case

Het project gebruikt strictFunctionTypes vanaf het begin. Bij het toevoegen van nieuwe handlers worden alle discrepanties tussen types automatisch ontdekt door de compiler. De code wordt robuuster tegen typefouten en is gemakkelijker te onderhouden.

Voordelen:

  • Betrouwbaarheid
  • Veiligheid van functie- en handleroverdracht
  • Voorspelbaar gedrag bij refactoren

Nadelen:

  • Vereist zorgvuldige planning van handtekeningen
  • In sommige gevallen moet je extra wrappers of overloads schrijven voor compatibiliteit