Отличный ответ @ jcalz объясняет, почему то, что вы пытаетесь сделать, так сложно. Его подход может сработать, если вы хотите сохранить базовые схемы и JSON одинаковыми. Но я отмечу, что вы можете обойти всю проблему, просто по-другому структурируя свои данные. Я думаю, что это улучшит ваш опыт разработчика, а также проясняет , что такое данные.
Одна из основных проблем, с которыми вы сталкиваетесь, заключается в том, что вы пытаетесь рассматривать карту из key: value
пар как, в вашем случае, своего рода список непроходимых плиток. Но по сути своей громоздко и запутанно работать с Object.entries
только для того, чтобы добраться до ваших непроходимых типов плиток.
Почему бы не определить ImpassableTile
как тип, а список непроходимых плиток как массив этого типа ? Это лучше соответствует концептуально тому, что на самом деле представляют данные. Он также полностью обходит Object.entries
и его трудности и делает перебор данных более простым и понятным.
// levelData.ts
import levelJSON from "./levelJSON.json";
interface ILevelJSON {
[key: string]: Level;
}
interface Level {
tiles: Tiles;
}
export type UnpassableType = "rock" | "tree";
type UnpassableTile = {
type: UnpassableType;
position: number[];
};
interface Tiles {
unpassable_tiles: UnpassableTile[];
}
export default levelJSON as ILevelJSON;
Чтобы должным образом соответствовать новому интерфейсу, вам необходимо изменить уровень JSON. json тоже. Но обратите внимание, что он намного чище, и вам не нужно определять пустые массивы для камней или деревьев на уровне_2, они просто отсутствуют:
{
"level_1": {
"tiles": {
"unpassable_tiles": [
{ "type": "rock", "position": [0, 0] },
{ "type": "rock", "position": [2, 0] },
{ "type": "tree", "position": [2, 2] }
]
}
},
"level_2": {
"tiles": {
"unpassable_tiles": []
}
}
}
Теперь вы можете очень легко нанести на карту непроходимые плитки , их типы и связанные данные о местоположении, при этом сохраняя полный вывод типов и безопасность. И это выглядит намного яснее и понятнее IMO.
// App.tsx
const UnpassableTileComponents = React.useMemo(() => {
return levelData[`level_1`].tiles.unpassable_tiles.map(
({ type, position: [leftPos, topPos] }) => (
<UnpassableTile
key={`level_1_${type}_${leftPos}_${topPos}`}
leftPos={leftPos}
topPos={topPos}
tileType={type}
/>
)
);
}, []);
https://codesandbox.io/s/goofy-snyder-u9x60?file= / src / App.tsx
Вы можете еще больше расширить эту философию к тому, как вы структурируете свои уровни и их интерфейсы. Почему бы не сделать levelJSON
массивом Level
объектов, каждый из которых имеет имя и набор плиток?
interface Tiles {
unpassable_tiles: UnpassableTile[];
}
interface Level {
name: string;
tiles: Tiles;
}
export type UnpassableType = "rock" | "tree";
type UnpassableTile = {
type: UnpassableType;
position: number[];
};
Ваши соответствующие данные будут выглядеть намного чище:
[
{
"name": "level_1",
"tiles": {
"unpassable_tiles": [
{ "type": "rock", "position": [0, 0] },
{ "type": "rock", "position": [2, 0] },
{ "type": "tree", "position": [2, 2] }
]
}
},
{
"name": "level_2",
"tiles": {
"unpassable_tiles": []
}
}
]
И повторение по нему стало бы еще более ясным:
const level = levelData[0];
const UnpassableTileComponents = React.useMemo(() => {
return level.tiles.unpassable_tiles.map(
({ type, position: [leftPos, topPos] }) => (
<UnpassableTile
key={`${level.name}_${type}_${leftPos}_${topPos}`}
leftPos={leftPos}
topPos={topPos}
tileType={type}
/>
)
);
}, [level]);
https://codesandbox.io/s/hopeful-grass-dnohi?file= / src / App.tsx