Проблема вывода типа с массивом универсальных объектов конфигурации с разными внутренними типами, которые зависят от другого внутреннего свойства

Я пытаюсь создать общую функцию, которая будет создавать довольно распространенные эффекты, поэтому я смогу передать только конфигурацию, и она создаст эффект.

Вот некоторые импортированные данные из NgRx:

import { ActionCreator } from '@ngrx/store';
import { createActionGroup, props } from '@ngrx/store';

и одна ActionGroup для целей тестирования:

export const featureActions = createActionGroup({
  source: 'MyGroup',
  events: {
    'Update Entity': props<{ data: { updateName: string } }>(),
    'Delete Entity': props<{ data: { deleteName: string } }>(),
  },
});

Для этого я создал файл конфигурации, который принимает действие ngrx (ActionCreator) и одну функцию сопоставления.

export type Config<AC extends ActionCreator> = {
  action: AC;
  mapping: (data: ReturnType<AC>) => string;
};

Сама фабричная функция принимает массив этих конфигов:

export function createMyEffects<AC extends ActionCreator>(
  effects: Config<AC>[]
) {
  // doing nothing, I guess it is not relevant
  console.info(effects);
}

Проблема возникает при вызове функции:

createMyEffects([
  {
    action: featureActions.updateEntity,
    mapping: (updateData) => updateData.data.updateName, // place #2
  },
  {
    // Issue is that it thinks that this action should be the same as the first
    action: featureActions.deleteEntity, // the place #1
    mapping: (deleteData) => deleteData.data.deleteName, // place #3
  },
]);

Итак, с моим объявлением функции TS выбрасывает меня на место №1, что typeof FeatureAtions.deleteEntity не может быть присвоен typeof FeatureAtions.updateEntity.

Поэтому я изменил его на

export function createMyEffects<AC extends ActionCreator[]>(
  effects: Config<AC[number]>[],
)

и в данном случае ошибок нет, но теперь я теряю вывод типа в местах №2 и №3. deleteData и updateData аргументы выводят тип Object вместо соответствующих ReturnType< FeatureAtions.deleteEntity> и ReturnType<FeatureAtions.updateEntity>.

Как я могу обеспечить вывод типа в этом случае?

УПД: Вот ссылка stackblits с env

Это минимально воспроизводимый пример ? Являются ли FeatureAtions, ActionCreator и ToastEffectConfig частными типами или они экспортируются с помощью angular/ngrx? Если они частные, пожалуйста отредактируйте вопрос, чтобы объявить их (а еще лучше удалите их и замените на родные TS). Если они связаны с технологией, от которой зависит вопрос, пожалуйста, import укажите их явно в примере. Цель состоит в том, чтобы мы могли скопировать и вставить ваш код в наши собственные IDE, сразу же увидеть вашу проблему и приступить к работе над ней.

jcalz 19.06.2024 18:06

Спасибо за обновления, но кое-чего еще не хватает. Пожалуйста, опробуйте свой собственный пример кода в автономной IDE и исправьте любые опечатки, несоответствия или недостающие вещи, о которых вы не спрашиваете. (например, data? Toast? updateName? и еще что-нибудь?)

jcalz 19.06.2024 18:27

Я вижу три ошибки, где вы упоминаете три ошибки, но все эти ошибки — опечатки. Вы пишете actions вместо action и data вместо updateData и deleteData. И updateName и deleteName больше нигде в коде не встречаются. Когда я попросил вас опробовать свой собственный код в автономной IDE… вы это сделали? Я действительно пытаюсь помочь, но это очень сложно, когда мне приходится отлаживать способ решения проблемы.

jcalz 19.06.2024 20:03

@jcalz Я добавил ссылку на stackblitz, надеюсь, это поможет воспроизвести задачу.

Dmytro 20.06.2024 10:57

Но код должен быть здесь в виде открытого текста. У меня нет прав копировать сюда код из вашего stackblitz. Не могли бы вы отредактировать, чтобы это сделать? И даже в этом стекеблице я вижу actionMapping где mapping ожидается? Я не знаю, как еще попросить вас исправить опечатки, прежде чем привлекать внимание других к коду. Все, что он делает, это замедляет людей и заставляет их решать проблемы, о которых вы даже не пытаетесь спросить. Не могли бы вы отредактировать приведенный здесь код в виде обычного текста и четырежды проверить его, чтобы убедиться в отсутствии опечаток?

jcalz 20.06.2024 14:21

(см. предыдущий комментарий.) Соответствует ли этот подход вашим потребностям? Если да, я напишу ответ, и если вы дадите мне разрешение, я также отредактирую код вашего вопроса, чтобы предоставить соответствующий минимально воспроизводимый пример без опечаток или недостающих объявлений. Если нет, то что мне не хватает?

jcalz 20.06.2024 14:26

@jcalz, да, спасибо, твой подход соответствует тому, что я искал. Извините за опечатки, и вы определенно можете отредактировать мой вопрос.

Dmytro 20.06.2024 15:16

Спасибо! Я напишу ответ, когда у меня будет возможность.

jcalz 20.06.2024 15:20

@jcalz, единственное, что осталось, это то, что даже ваш пример не выводит типы машинописного текста v5.2.2. Есть ли у вас идеи, почему и есть ли способ справиться с этим в версии 5.2.2, или единственный способ — перейти на версию 5.4.5?

Dmytro 20.06.2024 15:21

Я не знаю, это действительно выходит за рамки. Я не вижу в вопросе ничего, указывающего конкретную версию TypeScript. Я подожду и не буду писать здесь ответ, пока не узнаю, достаточно ли это важно для вас, чтобы редактировать вопрос, чтобы указать TS5.2. Дайте мне знать, как действовать.

jcalz 20.06.2024 15:32

Я понятия не имел, что эта версия может иметь отношение к проблеме. Я приму ваш ответ, потому что независимо от версии ваш код работает, а мой — нет. Было бы здорово, если бы у вас были какие-нибудь идеи относительно того, как я мог бы сделать это с помощью v5.2.2.

Dmytro 20.06.2024 15:43

Конечно, кажется, что что-то изменилось в TS5.4, чтобы он начал работать, но в примечаниях к выпуску TS5.4 это не упоминается, а это означает, что мне нужно начать копать, чтобы увидеть, какой именно коммит изменил ситуацию, и посмотреть, с чем это связано. прежде чем я смог даже предположить, как заставить его работать в предыдущих версиях. Я не думаю, что смогу потратить столько времени на его просмотр, по крайней мере, не сейчас.

jcalz 20.06.2024 15:45

Нет, нет. Огромное спасибо, все в порядке.

Dmytro 20.06.2024 15:51

Обратите внимание, что соответствующее изменение обсуждается здесь . Если вам необходимо использовать TS5.2, я бы предложил использовать параметр rest вместо ввода массива, как показано в этой ссылке на игровую площадку. Я, вероятно, не буду включать ничего из этого в свой ответ.

jcalz 20.06.2024 19:24

Потрясающий. Спасибо за ваши усилия. Будем ждать вашего ответа, чтобы отметить вопрос как решенный.

Dmytro 20.06.2024 20:45
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
1
15
73
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Если вы хотите, чтобы каждый элемент входного массива effects имел свой собственный аргумент типа для Config<>, тогда вам нужно будет сделать функцию универсальной в кортежном типе этих аргументов типа и сделать effects сопоставленным тип кортежа:

function createMyEffects<AC extends ActionCreator[]>(
    effects: { [I in keyof AC]: Config<AC[I]> }
) {
    console.info(effects);
}

Тогда это будет работать по желанию, по крайней мере, для TypeScript 5.4 и выше:

createMyEffects([
    {
        action: featureActions.updateEntity,
        mapping: (updateData) => updateData.data.updateName,
    },
    {
        action: featureActions.deleteEntity,
        mapping: (deleteData) => deleteData.data.deleteName,
    },
]);

К сожалению, до TypeScript 5.4 это не работало должным образом... TypeScript имеет возможность делать выводы из сопоставленных типов, но когда ввод функции представляет собой литерал массива это работало неправильно. Это было исправлено в microsoft/TypeScript#56555.

Детская площадка, ссылка на код

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