#typescript
#машинописный текст #typescript
Вопрос:
Предположим, существует функция с несколькими сложными требуемыми универсальными типами. Допустим, есть сценарий, в котором мы хотим иметь возможность использовать эту функцию снова и снова, но мы знаем, что некоторые дженерики всегда будут одинаковыми. Есть ли способ определить промежуточную функцию, которую вместо этого можно вызвать, которая определяет, каковы эти известные обобщения, и в то же время допускает применение других обобщений?
Примерный случай:
Существует функция sayHello следующим образом:
declare function sayHello<A, B, C>(arg1: A, arg2: (arg: B) => C, /* multiple complex args... */) : C[]
Предположим, я хочу иметь возможность использовать эту функцию несколько раз, но большинство дженериков всегда одни и те же. Я бы подумал, что есть способ предопределить «расширение» для этой функции следующим образом.
const sayNumericalHello<X> = sayHello<X,number, number>
Чтобы ее можно было использовать повторно только с одним обобщением:
sayNumericalHello<string>(/* args... */)
sayNumericalHello<number>(/* args... */)
sayNumericalHello<boolean>(/* args... */)
Возможно ли это? Я довольно новичок в typescript, так что извините, если это элементарно, но я нигде не смог найти подобного «наследования функций».
Комментарии:
1. Нет, типы более высокого порядка не существуют в TS
Ответ №1:
Вы можете добиться аналогичного результата, указав значения по умолчанию для переменных типа B и C. Поскольку вы говорите, что в большинстве случаев вам нужны значения по умолчанию, это может сработать для вас.
declare function sayHello<A, B = number, C = number>(arg1: A, arg2: (arg: B) => C, /* multiple complex args... */) : C[];
Затем вы просто вызываете метод с параметрами, и первый тип будет выведен из переданного вами параметра, а остальные, как ожидается, будут иметь тип номера по умолчанию, если вы не укажете иное. Вы не должны указывать какие-либо параметры типа при использовании функции, чтобы это работало, иначе значения по умолчанию не используются, и вам нужно указать их все. Вот несколько примеров вызовов для ваших сценариев.
sayHello('test', (a) => a); //string
sayHello(123, (a) => a); // number
sayHello(true, (a) => a); // boolean
Другой вариант — определить функцию только с одним параметром типа, который вызывает другую, более универсальную функцию, это немного менее эффективно, поскольку вы выполняете другой вызов функции только для того, чтобы получить нужную подпись. Например:
function sayNumericalHello<A>(arg1: A, arg2: (arg: number) => number, /* multiple complex args... */) : number[] {
return sayHello(arg1, arg2, /* multiple complex args... */)
}
Комментарии:
1. Можно ли выполнить первое решение, если функция из пакета npm?
2. Можете ли вы предоставить больше информации о пакете? Это пакет, который, я думаю, создан не вами.
3. Мой конкретный вариант использования — это попытка расширить
@reduxjs/toolkit
‘screateAsyncThunk
. У меня есть несколько случаев, когда я использую в основном одни и те же параметры, но всего несколько разных (например, ожидалstring[], (the generic type:) T[], MyAppState
). С точки зрения «работы» копировать и вставлять не так уж много, но для стандартизации, а не дублирования кода это было бы большой победой, я думаю.4. Я посмотрел на определение типа для этой библиотеки, и оно очень сложное, поэтому, на мой взгляд, лучше всего, если вы возьмете мое второе решение и объявите свою собственную функцию, которая вызывает другую функцию. Можно изменять типизацию с помощью расширения модуля, но в данном случае я не думаю, что это практично, потому что в определении функции
createAsyncThunk
также используется много внутренних типов. Вам придется дублировать все, а не только определение функции, поэтому каждый раз, когда что-то в библиотеке меняется, будет много работы.5. Спасибо за помощь. Вы определенно правы в том, что второе решение является лучшим маршрутом!
Ответ №2:
Одним из решений было бы использовать каррирование:
вместо функции с несколькими сложными аргументами вы получили функцию с одним аргументом, которая сама возвращает функцию с одним аргументом.
Таким образом, универсальный тип может быть добавлен к каждому уровню функции соответствующим образом.
на примере :
declare function sayHello<A, B, C>(arg1: A, arg2: (arg: B) => C, /* multiple complex args... */) : C[]
может быть преобразован как
declare function sayHello<A>(arg1: A) : <B, C>(arg2: (arg: B) => C, /* complex args... */) => C[]
использование становится
sayHello("")((arg: number) => {})