TypeScript: параметры функции переноса в Promise<T>

Я пытаюсь создать тип, который принимает тип функции, оборачивает параметры функции в Promise<> и возвращает новый тип — ту же функцию, но с параметрами, как у Promise, например:

type PromisedFn = PromisedArgs<(a: number, b: string | number) => void>
// should produce (a: Promise<number>, b: Promise<string | number>) => void

После нескольких часов ломания головы мне удалось получить только такой код:

type Func = (id: number, guid: number | string) => number | string; 
type FuncParams = Parameters<Func>;
// FuncParams = [id: string, guid: string | number]

type FuncWrappedParams = {
  [K in keyof FuncParams as Extract<K, '0'|'1'>]: Promise<FuncParams[K]>
}
// { 0: Promise<number>, 1: Promise<string | number> }

Тем не менее объект с числовым индексом не может быть правильно применен как массив:

type FuncWrappedParamsArray = [...args: FuncWrappedParams[]];
type WrappedParamsFunc = (...args: FuncWrappedParamsArray) => ReturnType<Func>;

let func: WrappedParamsFunc = (
  id: Promise<number>,
  guid: Promise<number | string>
) => 'TestString';

// ERROR:
// Type '(id: Promise<number>, guid: Promise<number | string>) => string'
// is not assignable to type 'WrappedParamsFunc'.
// Types of parameters 'id' and 'args' are incompatible.
// Type 'FuncWrappedParams' is missing the following properties from type 'Promise<number>':
// then, catch, [Symbol.toStringTag]

Я понятия не имею, как справиться с этим.

Проблема №1 описана выше.

Проблема №2: недостаток в том, что мы должны заранее знать ряд параметров функции: [K in keyof FuncParams as Extract<K, '0'|'1'>].

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
0
43
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Вы можете написать PromisedArgs так:

type PromisedArgs<T extends (...args: any) => any> =
  T extends (...args: infer A) => infer R ? (
    (...args: { [I in keyof A]: Promise<A[I]> }) => R
  ) : never;

Пока ваш тип массива является параметром типа универсальный (например, A выше), сопоставленный тип поверх него создаст другой тип массива. В {[I in keyof A]: ...} тип ключа I, по сути, автоматически перебирает числовые индексы. Вам не нужно вручную захватывать "0" | "1" | "2" | ... или беспокоиться о том, как продвигать полученный сопоставленный тип обратно в массив.

Использование ключевое слово infer в проверке условного типа — это просто способ одновременно получить параметры и возвращаемый тип функции вместо использования Parameters<T> и ReturnType типы утилит по отдельности. Если вы посмотрите на определения из Parameters и из ReturnType, вы увидите, что они реализованы одинаково.

В любом случае, давайте удостоверимся, что это работает так, как задумано:

type Func = (id: number, guid: number | string) => number | string;
type WrappedParamsFunc = PromisedArgs<Func>
/* type WrappedParamsFunc =
   (id: Promise<number>, guid: Promise<string | number>) => string | number */

let func: WrappedParamsFunc = (
  id: Promise<number>,
  guid: Promise<number | string>
) => 'TestString';

Выглядит неплохо!


Кстати, в TypeScript есть ошибка, о которой сообщалось на Майкрософт/TypeScript#27995, которая могла помешать вам найти это решение самостоятельно. Оказывается, важно, чтобы вы отображали ключи массива, подобного параметр универсального типа. Если вы попытаетесь сопоставить определенный тип, сопоставление вернется к повторению всех ключей, даже методов массива и прочего:

type Foo = [1, 2, 3]
type Bar = { [I in keyof Foo]: Promise<Foo[I]> }; 
/* type Bar = {
    [x: number]: Promise<3 | 1 | 2>;
    0: Promise<1>;
    1: Promise<2>;
    2: Promise<3>;
    length: Promise<3>;
    toString: Promise<() => string>; 
    ...
*/

Поскольку Foo — это конкретный тип, а не параметр типа, вы получаете путаницу выше. Ваш FuncParams относится к определенному типу, как и Foo, и вы пытались навести порядок вручную, что не совсем сработало. Ну что ж!

Ссылка на код для игровой площадки

Итак, из-за ошибки, когда Любые я хочу создать тип массива из другого типа массива, мне нужен этот условный трюк, верно? (Это действительно то место, где я застрял - и, как вы говорите, я тоже думаю, что OP - когда я пытался решить это вчера.) Знаете ли вы, есть ли у нас чистый "Как мне создать новый тип массива из тип массива?" вопрос с этим в качестве ответа? Я вчера не смог найти.

T.J. Crowder 17.03.2022 08:11

@T.J.Crowder, ну, есть stackoverflow.com/questions/66429203/…, но я не знаю, соответствует ли это вашему критерию.

jcalz 17.03.2022 14:05

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