TS: создание сопоставленного типа из ключей динамического объекта - PullRequest
0 голосов
/ 23 октября 2019

Я понимаю, что .reduce довольно сложно набрать в TS, и то, что я пытаюсь сделать, может оказаться невозможным. Но в любом случае, я хотел бы получить совет о том, как лучше набрать следующее:

Допустим, у меня есть массив people:

  const people = [
    { id: '1' },
    { id: '2' },
    { id: '3' },
  ]

Я делаю форму сFormik чтобы вы могли ввести имя для этих людей. Мне бы хотелось, чтобы values этой формы было структурировано как-то вроде values.1.name, где 1 - это id этого человека.

  const initialValues = people.reduce((acc, p) => ({
    ...acc,
    [p.id]: {
      name: '',
    },
  }), {});

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

  interface Values {
    [key: '1' | '2' | '3']: string;
  }

Проблема в том, что по умолчанию .reduce будет набирать my initialValues и в итоге будет набираться как {}, поэтому пытаемся получить доступ values[person.id] приведет к ошибке.

Я видел, что вы можете сделать это:

const peopleIds = {
    one: true,
    two: true,
    three: true,
  };

  type PeopleIds = keyof typeof peopleIds;

Это может быть очень полезно, если мой список people всегда будет статичным. По сути, я хочу, чтобы Values был напечатан как объект, где все возможные ключи имеют значения people.id.

Понятно, что это немного сложно, но мне интересно, выполнимо ли это даже в TS, так какЯ буду полагаться на такие вещи, как .reduce, чтобы генерировать схему моей формы и все такое.

Спасибо!

1 Ответ

0 голосов
/ 23 октября 2019

Вы можете представлять тип при условии, что вы отслеживаете литеральные значения в свойствах id элементов массива people. Самый простой способ сделать это с помощью const утверждения :

const people = [
    { id: '1' },
    { id: '2' },
    { id: '3' },
] as const;

Тогда тип, который вы ищете:

type InitValueType = Record<typeof people[number]['id'], { name: string }>;
/* type InitValueType = {
    1: {
        name: string;
    };
    2: {
        name: string;
    };
    3: {
        name: string;
    };
} */

И это легкодостаточно использовать утверждение типа , чтобы получить вывод reduce(), который вам нужен:

const initialValues = people.reduce((acc, p) => ({
    ...acc,
    [p.id]: {
        name: '',
    },
}), {} as InitValueType);

Это утверждение типа не является полностью безопасным для типа;это не останавливает некоторые плохие вещи:

const badInitialValues = people.reduce((acc, p) => ({
    ...acc,
    [p.id]: {
        kablam: '', // what?
    },
}), {} as InitValueType);

Но это может быть лучшим, что вы можете разумно сделать здесь, поскольку пытаетесь дать типизацию для reduce(), которая представляет один обратный вызов как представление каждогозвено в цепочке типов, начинающееся с {} и заканчивающееся InitValueType, будет невероятно грязным и вряд ли в конечном итоге принесет вам больше безопасности.

Хорошо, надеюсь, это поможет;удачи!

Ссылка на код

...