См. следующий пример кода:
type Properties = {
item0: { item0: string };
item1: { item1: string };
item2: { item2: string };
item3: { item3: string };
item4: { item4: string };
};
type Func<N extends keyof Properties> = ({}: Properties[N]) => number;
const Wrap = <N extends keyof Properties>(inner: Func<N>): Func<N> => {
return (v: Properties[N]) => inner(v) + 2;
};
type FuncSet = { [Property in keyof Properties]: Func<Property> };
const WrapSet = (inner: FuncSet): FuncSet => {
return {
item0: Wrap(inner.item0),
item1: Wrap(inner.item1),
item2: Wrap(inner.item2),
item3: Wrap(inner.item3),
item4: Wrap(inner.item4),
};
};
Как можно провести рефакторинг WrapSet так, чтобы он перебирал элементы в свойствах, чтобы их не приходилось указывать явно? Я ищу ответ, который не обходит систему типов машинописного текста, поскольку это вопрос машинописного текста, а не вопрос Javascript.
jcalz, если ты считаешь, что это лучший ответ, то тебе следует отправить его






На самом деле вы спрашиваете, как перебирать все свойства объекта. Это можно сделать с помощью метода Object.keys(myObj), который возвращает массив всех свойств, определенных в myObj (не включая реквизиты из прототипа объекта).
Доступ к значению каждого свойства можно получить через квадратные скобки.
Итак, вы можете сделать что-то вроде этого:
const WrapSet = (inner: FuncSet): FuncSet => {
return Object.keys(inner).reduce((acc, key) => {
acc[key] = Wrap(inner[key]);
return acc;
}, {});
};
В целом верно, но Typescript не очень нравится этот ответ: tsplay.dev/mb21om
Да, ты прав. Может потребоваться приведение типов
В TypeScript нет хорошего способа сделать это без использования какого-либо механизма ослабления типов, такого как утверждения типа . Язык не обладает достаточной выразительностью, необходимой для абстрагирования отображаемых типов в целом, особенно без типов более высокого рода, как это требуется в microsoft/TypeScript#1213.
я бы с удовольствием написал
const WrapSet = (inner: FuncSet): FuncSet =>
Object.fromEntries(Object.entries(inner).map(([k, v]) => [k, Wrap<any>(v)])) as
unknown as FuncSet;
используя Object.entries() , чтобы разделить объект на его свойства, затем сопоставить их с новыми свойствами и, наконец, использовать Object.fromEntries() , чтобы снова построить объект. Во время выполнения это работает без проблем, но я использовал утверждения типа и любой тип, чтобы избежать ошибок типа.
Если вы попытаетесь сделать этот тип более безопасным, вы обнаружите, что продолжаете сталкиваться с препятствиями, связанными с генериками высшего порядка дженериков и их ограничениями. Вы хотели бы сказать, что WrapSet — это особая форма более общего картографа mapValues():
function mapValues<T extends object>(obj: T, map: <K extends keyof T>(v: T[K]) => T[K]) {
return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, map(v)])) as T
}
но убедить компилятор в том, что Wrap имеет правильный тип, по сути невозможно:
const WS = (inner: FuncSet) => mapValues(inner, Wrap); // error!
// -------------------------------------------> ~~~~
Правильный тип выглядит так:
const Wrap = <N extends keyof Properties>(inner: FuncSet[N]): FuncSet[N] => {
return (v: Properties[N]) => inner(v) + 2; // error!
//~~~~~~
};
Но опять же, компилятор недоволен. Здесь просто невозможно следовать отношениям высшего порядка. Возможно, вы сможете поиграть в некоторые игры с дженериками и приблизиться к этому, но мы уже далеко прошли точку убывающей отдачи. Лично я бы придерживался версии утверждения типа до тех пор, пока в TypeScript не будут реализованы дженерики более высокого порядка (возможно, этого не произойдет никогда).
Вы не можете абстрагироваться от сопоставленных типов, чтобы избежать утверждений типов. Вы могли бы написать этот код, но TS безнадежно теряется, когда дело доходит до проверки правильности. Это полностью решает вопрос? Если да, то я мог бы написать ответ (существующий ответ делает то же самое, но, похоже, не касается набора текста). Если нет, то чего мне не хватает?