Явный возвращаемый тип на основе аргументов

#typescript #typescript-typings #typescript-generics

#typescript #typescript-типизации #typescript-дженерики

Вопрос:

Дана функция, которая принимает массив объектов и возвращает объект, все ключи которого соответствуют свойству каждого из объектов в аргументе… похоже, должен быть способ получить более явный возвращаемый тип.

Функция-

 function objectFromArray(arr: { key: string }[]) {
    let obj: {[k: string]: true} = {};
    for (let {key} of arr) {
        obj[key] = true;
    }
    return obj;
}
 

Если я выполню следующее

 let myArray = [{key: 'a'}, {key: 'b'}, {key: 'c'}];
let myObj = objectFromArray(myArray);
 

Тогда тип myObj

 let myObj: {
    [k: string]: true;
}
 

Но я чувствую, что имеется достаточно информации, чтобы typescript мог сделать вывод, что тип myObj на самом деле

 let myObj: {
    a: true;
    b: true;
    c: true;
}
 

Мой вопрос заключается в том, что нужно сделать в objectFromArray функции, чтобы получить явный возвращаемый тип на основе аргумента. Для ясности я не беспокоюсь о значениях возвращаемого типа, просто беспокоюсь о получении явных ключей.

Ответ №1:

Если вы хотите, чтобы это работало, вам нужно objectFromArray() быть универсальной функцией в типе K строковых литеральных значений в key свойстве arr элементов:

 function objectFromArray<K extends string>(arr: readonly { key: K }[]) {
  let obj = {} as { [P in K]: true }; // have to assert this because it's not true yet
  for (let { key } of arr) {
    obj[key] = true;
  }
  return obj;
}
 

readonly In readonly { key: K }[] просто означает, что мы специально не требуем, чтобы передаваемый массив был изменяемым. Это дает больше возможностей для ввода, которые нам понадобятся немного позже.

Возвращаемый тип — { [P in K]: true } это сопоставленный тип, предоставляющий объект со свойством со значением a true для каждого строкового литерала, вводимого в K . (Вы также можете использовать Record тип утилиты для предоставления эквивалента Record<K, true> ). Поскольку {} значение этого типа недопустимо, вам потребуется утверждение типа, чтобы сообщить компилятору, что он должен обрабатывать obj как тип { [P in K]: true } .


Затем, когда вы создаете myArray , поведение компилятора по умолчанию заключается в расширении его типа до Array<{key: string}> , полностью забывая о строковых литеральных типах "a" , "b" , и "c" . Чтобы предотвратить это, вы можете использовать const утверждение:

 let myArray = [{ key: 'a' }, { key: 'b' }, { key: 'c' }] as const;

/* let myArray: readonly [{
    readonly key: "a";
}, {
    readonly key: "b";
}, {
    readonly key: "c";
}] */
 

Обратите внимание, как const утверждение создает myArray readonly массив, поэтому полезно ослабить тип ввода функции. Теперь, когда компилятор знает достаточно myArray , давайте вызовем objectFromArray() :

 let myObj = objectFromArray(myArray);
/* let myObj: {
    a: true;
    b: true;
    c: true;
} */
 

Успех! myObj известно, что имеет a , b , и c свойства типа true , по желанию.

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