Каков способ определения объекта с динамическим числом свойств?

#typescript

#typescript

Вопрос:

Я пытаюсь присвоить правильную форму объекту, который служит в качестве начального значения метода reduce . Этот объект может иметь динамическое количество свойств, а именем ключа могут быть числа от 1 до 6, последнее определяется в типе DiceNumber

 type DiceNumber = 1 | 2 | 3 | 4 | 5 | 6;

const dataset: DiceNumber[] = [];

function rollDice(): DiceNumber {
  return Math.floor(Math.random() * 6   1) as DiceNumber;
}

function stats(data: DiceNumber[]) {
  return data.reduce((a, c) => {
    a[c] = (a[c] || 0)   1;

    return a;
  }, {} as any);
}

dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());
dataset.push(rollDice());

console.log(stats(dataset));
 

Не используя any, я получаю следующее сообщение

Элемент неявно имеет тип ‘any’, поскольку выражение типа ‘DiceNumber’ нельзя использовать для индексации типа ‘{}’. Свойство ‘1’ не существует для типа ‘{}’

При попытке определить интерфейс следующим образом

 interface MyType {
  [key: DiceNumber]: number;
}
 

Я получаю следующее сообщение

Тип параметра подписи индекса не может быть типом объединения. Вместо этого рассмотрите возможность использования сопоставленного типа объекта.

Ответ №1:

Тип, который вы ищете, действительно является отображенным типом. Я бы предложил:

 { [K in DiceNumber]?: number }
 

это означает, что для каждого ввода ключа K DiceNumber тип объекта имеет необязательное свойство type number . Вы также можете использовать встроенные типы утилит и вызывать их Partial<Record<DiceNumber, number>> .

Итак, ваша функция теперь:

 function stats(data: DiceNumber[]) {
    return data.reduce((a, c) => {
        a[c] = (a[c] || 0)   1;

        return a;
    }, {} as { [K in DiceNumber]?: number });
}
 

И вы можете проверить, что stats() сигнатура вызова

 /* function stats(data: DiceNumber[]): {
    1?: number | undefined;
    2?: number | undefined;
    3?: number | undefined;
    4?: number | undefined;
    5?: number | undefined;
    6?: number | undefined;
} */
 

как и ожидалось.

Игровая площадка ссылка на код

Ответ №2:

Попробуйте это:

 type Accumulator = {
    [K in DiceNumber]?: number
}

function stats(data: DiceNumber[]) {
  return data.reduce((a: Accumulator, c: DiceNumber) => {
    a[c] = (a[c] || 0)   1;
    return a;
  }, {});
}