javascript - betingede typeparametere i typescript


Jeg prøver å lage en funksjon der columns-parameteren avhenger av hvilken type som er valgt.

function useNavigation(type: "horizontal" | "grid" | "vertical", columns?: number)

Hvordan endre den typeerklæringen for å sette at kolonneparameteren er påkrevd når parameteren type er grid

Svar #1

Du kan bruke funksjonsoverlasting for å oppnå dette. I eksemplet mitt nedenfor spesifiserer de to første erklæringene forskjellige argumenter basert på dine krav, mens den siste erklæringen (og følgelig implementeringen) mottar alle mulige argumenter, noe som er grunnen til at type fortsatt er 'horizontal' | 'vertical' | 'grid'.

Oppringeren vil få riktige typer og autokomplettering, du må bare gjøre sjekkene inni funksjonen for å bestemme logikken din basert på argumentene.

function useNavigation(type: 'horizontal' | 'vertical');
function useNavigation(type: 'grid', columns: number);
function useNavigation(
  type: 'horizontal' | 'vertical' | 'grid',
  columns?: number,
) {
  // Your function body...
}

Her er en TS-lekeplass som demonstrerer dette.

Svar #2

Tradisjonelt (før TS 2.8 eller TS 3.0) kunne du bare bruke overbelastninger for å oppnå dette, men nå ville jeg være tilbøyelig til å bruke en tuple-typet restparameter; spesifikt en union av slike tuple-typer:

function useNavigation(...args:
    [type: "horizontal" | "vertical", columns?: number] |
    [type: "grid", columns: number]
) { }

Det betyr at argumentene til useNavigation() må enten være: et par hvor det første elementet er "horizontal" eller "vertical" og det andre elementet er en valgfri number-verdi; eller et par hvor det første elementet er "grid" og det andre elementet er en påkrevd number-verdi.

Du kan teste at det fungerer som ønsket:

useNavigation("horizontal") // okay
useNavigation("vertical", 20); // okay
useNavigation("grid", 10); //okay
useNavigation("grid"); // error!
// ---------> ~~~~~~
// Source has 1 element(s) but target requires 2

Merk at typesignaturen for useNavigation() også utnytter merkede tuppel-elementer, for å vise at vi har til hensikt at den som kaller funksjonen skal tenke på det første argumentet som å ha navnet type og det andre argumentet som å ha navnet columns. Slike etiketter er kun relevante når det gjelder typehint i IntelliSense og påvirker ikke typene eller kjøreatferden. Spesielt finnes det ingen variabel med navnet type eller columns her; funksjonsimplementeringen ser kun et args-array:

function useNavigation(...args:
    [type: "horizontal" | "vertical", columns?: number] |
    [type: "grid", columns: number]
) {    
    if (args[0] === "grid") {
        args[1].toFixed(); // okay
    } 
}

Du kan, hvis du vil, destrukturere args til type og columns variabler som i

const [type, columns] = args;

men det er en fordel å la det være som args; nemlig at kompilatoren ser args som en diskriminert unionstype. Merk at i implementasjonen ovenfor ser kompilatoren at hvis args[0] === "grid", så er args[1] definitivt et number og ikke muligens undefined. Sammenlign med oppførselen når du skiller args ut i to variabler som ikke lenger sees som korrelert:

if (type === "grid") {
    columns.toFixed(); // oops, possibly undefined
}

Kompatibilizeren ser bare columns som typen number | undefined uavhengig av om du først sjekker type === "grid" eller ikke. Dette er kanskje ikke en stor sak for deg, men diskriminerte unions er nyttige nok til at jeg ønsket å påpeke det.

Lenke til kode på lekeplassen

Relaterte artikler