typescript - Typvakt baserad på funktionsarity
Låt oss säga att jag har en funktion med flera valfria parametrar.
Varför finns det ingen typkontroll baserad på funktionens aritet genom arguments-nyckelordet och hur löser man detta utan att ändra implementationen och utan fula typomvandlingar?
function test(arg0?: string, arg1?: string, arg2?: string) {
if (arguments.length === 1) {
// I expect arg0 to be of type string instead of string | undefined
}
if (arguments.length === 2) {
// I expect arg0 and arg1 to be of type string instead of string | undefined
}
if (arguments.length === 3) {
// I expect arg0, arg1 and arg2 to be of type string instead of string | undefined
}
}
Svar #1
Vänligen se arguments-typ:
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
interface IArguments {
/** Iterator */
[Symbol.iterator](): IterableIterator<any>;
}
Som du kanske har märkt, vet nyckelordet arguments ingenting om aritet för argumenttyp. Ur TypeScript-perspektiv är det inte ens bundet till funktionskontext. Det är bara ett objekt med en massa egenskaper som inte är relaterade till funktionsargument.
Dessutom beter sig length-egendomen inte som en typvakt. Du kan inte dra slutsatsen om till exempel [string, string]-typen bara genom att kontrollera om length === 2. Vänligen se detta svar. Du kommer att hitta lämpliga länkar till GitHub-ärenden och denna hasLengthAtLeast-hjälpare där.
Svar #2
Det för närvarande accepterade svaret är ganska bra och använder en typ-predikat för att begränsa typen.
Jag skulle föreslå ett alternativt sätt, genom att använda restparametrar, på följande sätt:
function test(...args: string[]) {
if (args.length === 1) {
console.log('arg0:', typeof args[0]);
}
if (args.length === 2) {
console.log('arg0:', typeof args[0], 'arg1:', typeof args[1]);
}
if (args.length === 3) {
console.log('arg0:', typeof args[0], 'arg1:', typeof args[1], 'arg2:', typeof args[2]);
}
}
När det används med olika argument får vi följande resultat:
| Inmatning | Kompilerar? | Utdata |
|---|---|---|
test('one'); | ja | arg0: sträng |
test('one', 'two', 'three'); | ja | arg0: sträng arg1: sträng arg2: sträng |
test(1); | nej | fel TS2345: Argument av typen 'number' kan inte tilldelas parameter av typen 'string'. |
test(undefined); | nej | fel TS2345: Argument av typen 'undefined' kan inte tilldelas parametern av typen 'string'. |
En (möjligtvis stor) nackdel med detta tillvägagångssätt är att längden på args-arrayen inte är begränsad. Beroende på ditt användningsfall kan det vara tillräckligt att ignorera överflödiga argument, eller så kan du utföra en kontroll vid körning på följande sätt:
if (args.length > 3) {
throw new Error(`Expected 3 arguments, but got ${args.length}`);
}
Tankeväckande.
