TypeScript Interface-funktionstyp med generics
Jag hittade nyligen följande kodbyte i ett öppet källkodsarkiv:
interface Use<I, C = context<I>> {
<O>(fn: avvio.Plugin<O, I>, options?: O): C;
}
Förenklat som:
interface F<A> {
<B>(foo: A, bar: B): A;
}
Hur skulle jag gå tillväga för att anropa B generiken?
Jag har svårt att hitta ett exempel på användning av det och jag kan inte förstå det ens i TS Playground. Jag lade in den här koden i en Playground som du kan titta på här.
Svar #1
Det är ett gränssnitt för en funktion som tar en generisk parameter. Här är ett exempel:
const funcTwo: F<string> = <T>(foo: string, bar: T) => {
return `${foo} ${bar}`;
}
console.log(funcTwo('Hello', 7));
UPPDATERING: Lägger till ett praktiskt exempel som kan visa varför det skulle vara användbart. Det kan vara svårt att komma på ett exempel utan kontext, men detta är mitt bästa försök utan att tänka för mycket:
class UniqueCounter<T> {
private underlyingSet = new Set<T>();
constructor(
private hashingFunc: <V>(value: V) => T,
) {
}
add<V>(thing: V) {
const hash = this.hashingFunc(thing);
this.underlyingSet.add(hash);
}
count() {
return this.underlyingSet.size;
}
}
Denna klass försöker räkna hur många unika instanser av något du har. Men, standarduppsättningen använder === för objekt och det är inte vad jag vill. Istället har jag någon form av hash-algoritm för mina specifika objekt. Även om jag i vissa fall hashar till en sträng och i andra fall hashar till ett nummer (det jag hashar till är T).
Jag kan inte lägga till V till klassens generics eftersom det då bara skulle kunna räkna en typ av objekt. Kanske kan jag hasha Person och Employee och Equipment och Room genom att använda någon typ av unikt ID som jag har tilldelat alla de klasserna.
Jag kan inte lägga till T till hash-funktionens typargument eftersom jag behöver använda T för att definiera underlyingSet.
UPPDATERING2:
Ignorera den föregående uppdateringen eftersom den inte är relevant för frågan. Typen du visade:
interface F<A> {
<B>(foo: A, bar: B): A;
}
är inte lika med:
interface F<A, B> {
(foo: A, bar: B): A;
}
Låt oss låtsas att vi hade en hashingfabrik som magiskt kunde skapa hashingobjekt, men de kunde bara hasha till en typ av nyckel. Med andra ord, du skulle kunna skapa ett "hasha vad som helst till sträng"-objekt eller ett "hasha vad som helst till nummer"-objekt. Vi skulle kunna definiera klassen som:
class HashingFactory<T> {
createHasher<V>(): Hasher;
}
Men vad skulle returtypen vara? Hur skulle vi definiera Hasher? Om vi definierar den som:
interface Hasher<K, V> {
hash(value: V): K;
}
Då skapar vi något som bara kan hasha indata av en typ (t.ex. det kan bara hasha Employee till ett nummer). Men vår magiska hasher kan hasha vilket objekt som helst till ett nummer. Det rätta gränssnittet skulle vara:
interface Hasher<K> {
<V>hash(value: V): K;
}
Nu kan vi på ett korrekt sätt representera ett objekt som kan förvandla Employee eller Room eller Person (eller något annat) till ett nummer.
