Недавно я нашел следующий байт кода в репозитории с открытым исходным кодом:
interface Use<I, C = context<I>> {
<O>(fn: avvio.Plugin<O, I>, options?: O): C;
}
Упрощенно как:
interface F<A> {
<B>(foo: A, bar: B): A;
}
Как мне вызвать универсальный B
?
Я изо всех сил пытаюсь найти пример его использования, и я не могу понять его даже на игровой площадке TS. Я поместил этот код в игровую площадку, которую вы можете просмотреть здесь.
Это интерфейс для функции, которая принимает общий параметр. Вот пример:
const funcTwo: F<string> = <T>(foo: string, bar: T) => {
return `${foo} ${bar}`;
}
console.info(funcTwo('Hello', 7));
ОБНОВЛЕНИЕ: добавлен практический пример, который может показать, почему это было бы полезно. Может быть трудно придумать пример без контекста, но это моя лучшая попытка, не слишком долго думая:
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;
}
}
Этот класс пытается подсчитать, сколько уникальных экземпляров чего-то у вас есть. Однако набор по умолчанию использует ===
для объектов, а это не то, что мне нужно. Вместо этого у меня есть какой-то алгоритм хеширования для моих конкретных объектов. Хотя в некоторых случаях я хэширую в строку, а в других случаях я хэширую в число (то, что я хэширую, это T
).
Я не могу добавить V
к дженерикам класса, потому что тогда он сможет подсчитывать только один тип объекта. Может быть, я смогу хэшировать Person
, Employee
, Equipment
и Room
, используя какой-то уникальный идентификатор, который я присвоил всем этим классам.
Я не могу добавить T
к аргументам типа хеш-функции, потому что мне нужно использовать T
для определения underlyingSet
.
ОБНОВЛЕНИЕ2:
Игнорируйте предыдущее обновление, поскольку оно не имеет отношения к рассматриваемому вопросу. Тип, который вы показали:
interface F<A> {
<B>(foo: A, bar: B): A;
}
не эквивалентен:
interface F<A, B> {
(foo: A, bar: B): A;
}
Давайте представим, что у нас есть фабрика хеширования, которая может волшебным образом создавать объекты хеширования, но они могут хешировать только один тип ключа. Другими словами, вы можете создать объект «хешировать что угодно в строку» или объект «хэшировать что угодно в число». Мы могли бы определить класс как:
class HashingFactory<T> {
createHasher<V>(): Hasher;
}
Однако каким будет тип возвращаемого значения? Как бы мы определили Hasher
? Если мы определим его как:
interface Hasher<K, V> {
hash(value: V): K;
}
Затем мы создаем что-то, что может хешировать входные данные только одного типа (например, он может хэшировать только сотрудника в число). Но наш волшебный хэшер может превратить любой объект в число. Правильный интерфейс будет таким:
interface Hasher<K> {
<V>hash(value: V): K;
}
Теперь мы можем правильно представить объект, который может превратить Сотрудника, Комнату или Персону (или что-то еще) в число.
Добавлен пример, который может помочь прояснить
На самом деле, неважно, если посмотреть дальше на вопрос, мой пример совершенно бесполезен и касается чего-то другого.
Так что технически интерфейс может иметь и другие свойства. Функции в JS могут иметь свойства и другие методы, прикрепленные к ним, как объекты. Таким образом, функция может быть вызываемой, и B
может применяться только к функции. Однако функция может также иметь другие свойства и функции, которые относятся к A
. Я не могу вспомнить ни одного примера из реальной жизни.
Хорошо, добавил еще один пример, надеюсь, более понятный.
Спасибо за отличный ответ. Теперь это имеет большой смысл, и я смог использовать его в моем возможном объявлении типа: github.com/fastify/fastify/pull/1569#issuecomment-482422616
Итак, учитывая объект интерфейса Hasher
, есть ли способ вызвать универсальный V
? Игнорирование HashingFactory
.
@nomadoda Конечно, если бы у вас был хэшер, вам никогда не нужно было бы указывать V, потому что TS мог бы вывести его на основе того, что вы передали.
Я все еще пытаюсь понять, почему это было бы полезно. Не лучше ли было бы поставить его в первой строке рядом с родовым словом «А»?