Hur säkerställer man att värdena i en array är nycklarna i ett TypeScript-gränssnitt?
Jag försöker skapa en events-egenskap på Klass som innehåller en array av strängar som exakt matchar alla nycklar i ett givet gränssnitt. Så här:
interface Events {
one: (foo: string) => void
two: (bar: number) => void
}
class Klass {
protected readonly events: [keyof Events] = ['one', 'two']
}
Men ovanstående ger fel med följande:
[ts]
Type '["one", "two"]' is not assignable to type '["one" | "two"]'.
Types of property 'length' are incompatible.
Type '2' is not assignable to type '1'. [2322]
(property) Klass.events: ["one" | "two"]
Vad behövs här för att säkerställa att events-egenskapen returnerar en array som innehåller alla händelser?
Svar #1
Du kan nästan uttrycka detta i typ systemet (förutsatt TS3.0+) med villkorliga typer, med några förbehåll:
type Invalid<T> = ["Needs to be all of", T]
const arrayOfAll = <T>() => <U extends T[]>(
...array: U & ([T] extends [U[number]] ? unknown : Invalid<T>[])
) => array;
const arrayOfAllEventKeys = arrayOfAll<keyof Events>();
const goodEvents = arrayOfAllEventKeys('one', 'two'); // okay, type ['one', 'two']
const extraEvents = arrayOfAllEventKeys('one', 'two', 'three'); // error
// ~~~~~~~
// Argument of type "three" is not assignable to parameter of type "one" | "two"
const missingEvents = arrayOfAllEventKeys('one'); // error
// ~~~~~
// Argument of type "one" is not assignable to
// parameter of type ["Needs to be all of", "one" | "two"]
const redundantEvents = arrayOfAllEventKeys('one', 'two', 'one'); // no error
// doesn't enforce distinctness
Observera att goodEvents härleds till typen [‘one’, ‘two’], och det finns inget fel. Det är vad du vill. Du får fel på extra händelser och på saknade händelser.
Varning 1: Felet för saknade händelser är lite kryptiskt; TypeScript stödjer ännu inte anpassade felmeddelanden, så jag valde något som förhoppningsvis är någorlunda förståeligt (Argument av typen "one" kan inte tilldelas till parametern av typen ["Behöver vara alla av", "one" | "two"]).
Varning 2: Det finns inget fel för redundanta händelser. Det finns inget allmänt sätt som jag kan hitta för att kräva att varje parameter till arrayOfAllEventKeys är av en distinkt typ som inte kolliderar med vissa problem med rekursiva typer. Det är möjligt att använda överlagring eller andra liknande tekniker för att fungera för arrayer upp till en viss hårdkodad längd (säg 10), men jag vet inte om det skulle uppfylla dina behov. Meddela mig.
Hoppas det hjälper; lycka till!
Svar #2
Du sätter [] på fel plats.
interface Events {
one: (foo: string) => void
two: (bar: number) => void
}
class Klass {
protected readonly events: (keyof Events)[] = ['one', 'two']
}
Lägg märke till hur jag ändrar [keyof Events] till (keyof Events)[].
Kolla detta TypeScript-lekområde för att verifiera att det inte finns några fel.
