Я думаю, я бы написал так ... учитывая KV
, тип объекта ключ / значение c, передаваемый как элемент xs
аргумента fromArray()
(или объединение таких типов), тип KeyValToObj<T>
, соответствующий типу выходного объекта:
type KeyValToObj<KV extends { key: PropertyKey, value: any }> =
{ [K in KV['key']]: Extract<KV, { key: K }>['value'] };
Вы можете видеть, что если KV
является объединением, подобным {key: "a", value: string} | {key: "b", value: number}
, тогда KeyValToObj<KV>
будет иметь ключ K
для каждого key
свойства в KV
, и значением будет соответствующее value
свойство члена KV
с key
типа K
: {a: string; b: number}
.
Тогда я бы дал fromArray()
следующую подпись:
function fromArray<XS extends Array<{ key: K, value: any }>, K extends PropertyKey>(
xs: XS | []): KeyValToObj<XS[number]> {
const obj = {} as any;
for (const x of xs) {
obj[x.key] = x.value
}
return obj
}
С xs
входом типа XS
мы выводим KeyValToObj<XS[number]>
, тип объекта, соответствующий объединению элементов типы XS
. Нам нужно утверждение типа от до any
, чтобы реализация не жаловалась; компилятор не сможет проверить, что мы действительно возвращаем правильный тип вывода здесь, поэтому я не стал пытаться.
Есть несколько складок в этой сигнатуре, чтобы сделать вывод типа работающим так, как я want: тип K
кажется излишним (почему бы не заменить его на PropertyKey
?), но это подсказка, чтобы заставить компилятор выводить строковых литералов типов для свойств key
. И | []
кажется излишним (зачем явно разрешать пустой кортеж, когда это может быть XS
?), Но это подсказка компилятору для вывода типа кортежа вместо XS
неупорядоченного массива ... что само по себе кажется странным (почему мы заботимся о порядке XS
, когда мы используем только XS[number]
, тип элемента?), но является подсказкой для предотвращения нормализации компилятором типы элементов содержат все свойства. Если вы ослабите любое из этих ограничений, вы получите странное поведение вывода.
Хорошо, давайте попробуем:
const geometry = fromArray([
{ key: 'circle', value: { color: 'blue', radius: 3 } },
{ key: 'rectangle', value: { color: 'red', width: 3, height: 2 } },
{ key: 'line', value: { length: 5 } }
])
geometry.circle
// circle: { color: string; radius: number; }
geometry.circle.radius;
geometry.line
// line: { length: number; }
geometry.line.length;
geometry.rectangle
// rectangle: { color: string; width: number; height: number; }
geometry.rectangle.width;
Выглядит хорошо. Вы получаете все автозаполнения, которые вы хотели. Для чего стоит, если вы используете дубликат ключа, тип вывода имеет объединение соответствующих типов значений в этом ключе.
Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код