Тип функции интерфейса TypeScript с дженериками

Недавно я нашел следующий байт кода в репозитории с открытым исходным кодом:

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. Я поместил этот код в игровую площадку, которую вы можете просмотреть здесь.

Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
0
2 087
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это интерфейс для функции, которая принимает общий параметр. Вот пример:

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;
}

Теперь мы можем правильно представить объект, который может превратить Сотрудника, Комнату или Персону (или что-то еще) в число.

Я все еще пытаюсь понять, почему это было бы полезно. Не лучше ли было бы поставить его в первой строке рядом с родовым словом «А»?

E.Arrowood 10.04.2019 20:55

Добавлен пример, который может помочь прояснить

Pace 10.04.2019 21:54

На самом деле, неважно, если посмотреть дальше на вопрос, мой пример совершенно бесполезен и касается чего-то другого.

Pace 10.04.2019 21:55

Так что технически интерфейс может иметь и другие свойства. Функции в JS могут иметь свойства и другие методы, прикрепленные к ним, как объекты. Таким образом, функция может быть вызываемой, и B может применяться только к функции. Однако функция может также иметь другие свойства и функции, которые относятся к A. Я не могу вспомнить ни одного примера из реальной жизни.

Pace 10.04.2019 21:57

Хорошо, добавил еще один пример, надеюсь, более понятный.

Pace 10.04.2019 22:08

Спасибо за отличный ответ. Теперь это имеет большой смысл, и я смог использовать его в моем возможном объявлении типа: github.com/fastify/fastify/pull/1569#issuecomment-482422616

E.Arrowood 12.04.2019 05:27

Итак, учитывая объект интерфейса Hasher, есть ли способ вызвать универсальный V? Игнорирование HashingFactory.

nomadoda 11.05.2020 14:12

@nomadoda Конечно, если бы у вас был хэшер, вам никогда не нужно было бы указывать V, потому что TS мог бы вывести его на основе того, что вы передали.

Pace 14.05.2020 04:51

Другие вопросы по теме