Tipo de função de interface TypeScript com genéricos
Recentemente, encontrei o seguinte byte de código em um repositório de código aberto:
interface Use<I, C = context<I>> {
<O>(fn: avvio.Plugin<O, I>, options?: O): C;
}
Simplificado como:
interface F<A> {
<B>(foo: A, bar: B): A;
}
Como eu faria para invocar o genérico B?
Estou tendo dificuldade em encontrar um exemplo de uso disso e não consigo entender nem mesmo no TS Playground. Coloquei este código em um playground que você pode ver aqui.
Resposta #1
É uma interface para uma função que recebe um parâmetro genérico. Aqui está um exemplo:
const funcTwo: F<string> = <T>(foo: string, bar: T) => {
return `${foo} ${bar}`;
}
console.log(funcTwo('Hello', 7));
ATUALIZAÇÃO: Adicionando um exemplo prático que pode mostrar por que seria útil. Pode ser difícil criar um exemplo sem contexto, mas esta é a minha melhor tentativa sem pensar demais:
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;
}
}
Esta classe tenta contar quantas instâncias únicas de algo você possui. No entanto, o conjunto padrão usa === para objetos e isso não é o que eu quero. Em vez disso, eu tenho algum tipo de algoritmo de hashing para meus objetos específicos. Embora, em alguns casos eu faça o hash para uma string e em outros casos eu faça o hash para um número (a coisa na qual faço o hash é T).
Não posso adicionar V aos genéricos da classe porque então ela só conseguiria contar um tipo de objeto. Talvez eu possa fazer hash de Person, Employee, Equipment e Room usando algum tipo de ID único que eu tenha atribuído a todas essas classes.
Não posso adicionar T aos argumentos de tipo da função hash porque preciso usar T para definir underlyingSet.
ATUALIZAÇÃO2:
Ignore a atualização anterior, pois ela não é relevante para a questão em questão. O tipo que você mostrou:
interface F<A> {
<B>(foo: A, bar: B): A;
}
não é equivalente a:
interface F<A, B> {
(foo: A, bar: B): A;
}
Vamos fingir que tivéssemos uma fábrica de hashing que poderia criar objetos de hashing magicamente, mas eles só poderiam hash em um tipo de chave. Em outras palavras, você poderia criar um objeto "hash de qualquer coisa para string" ou um objeto "hash de qualquer coisa para número". Poderíamos definir a classe como:
class HashingFactory<T> {
createHasher<V>(): Hasher;
}
No entanto, qual seria o tipo de retorno? Como definiríamos Hasher? Se definirmos assim:
interface Hasher<K, V> {
hash(value: V): K;
}
Então estamos criando algo que só pode criar hash de entradas de um tipo (por exemplo, ele só pode criar hash de Employee em um número). Mas nosso hasher mágico pode criar hash de qualquer objeto em um número. A interface adequada seria:
interface Hasher<K> {
<V>hash(value: V): K;
}
Agora podemos representar adequadamente um objeto que pode transformar Empregado ou Sala ou Pessoa (ou qualquer outra coisa) em um número.
