English 中文(简体)
使用目标时的储备类型。
原标题:Preserve Type when using Object.entries

I m fairly new to TypeScript, so I m in the process of upgrading my old projects to utilize it.

然而,我不知道在发出反对时如何保持正确的类型。 一些数据条目。

CodeSandbox example

例如:

<>水平:

  const UnpassableTileComponents = useMemo(() => 
    Object.entries(levelData[`level_${gameLevel}`].tiles.unpassable_tiles).map(([tileType, tiles]) => (
      tiles.map(([leftPos, topPos], index) => (
        <UnpassableTile
          key={`${tileType}_${index}`}
          leftPos={leftPos * 40}
          topPos={topPos * 40}
          tileType={tileType}
        />
      ))
    )
  ).flat(), [gameLevel])

levelData.tsx:

import levelJSON from "./levelJSON.json";

interface ILevelJSON {
  [key: string]: Level;
}

interface Level {
  tiles: Tiles;
}

interface Tiles {
  unpassable_tiles: UnpassableTiles;
}

interface UnpassableTiles {
  rock: Array<number[]>;
  tree: Array<number[]>;
}

export default levelJSON as ILevelJSON;

levelJSON.json:

{
  "level_1": {
    "tiles": {
      "unpassable_tiles": {
        "rock": [[0, 0]],
        "tree": [[2, 0]]
      }
    }
  },
  "level_2": {
    "tiles": {
      "unpassable_tiles": {
        "rock": [[]],
        "tree": [[]]
      }
    }
  }
}

In the case of the above, tiles represents an Array of arrays, each with two numbers. Therefore, [leftPos, topPos] should both be typed as number. However, in Level.tsx, they have properties of any. I could get my desired result with the following:

  const UnpassableTileComponents = useMemo(() => 
    Object.entries(levelData[`level_${gameLevel}`].tiles.unpassable_tiles).map(([tileType, tiles]) => (
      tiles.map(([leftPos, topPos] : number[], index: number) => (
        <UnpassableTile
          key={`${tileType}_${index}`}
          leftPos={leftPos * 40}
          topPos={topPos * 40}
          tileType={tileType}
        />
      ))

But shouldn t number[] be inferred anyways?

任何建议都会得到赞赏。

最佳回答

@jcalz的出色回答解释了为什么你试图做的就是这样trick。 如果你想要保持你的基本图谋和初等人物,他的做法就会发挥作用。 但我要指出,只有以不同的方式整理你的数据,你才能回避整个问题。 我认为,这将使你的发展经验以及你的数据<>的澄清更好。

One of the fundamental problems you re having is that you re trying to treat a map of key: value pairs as, in your case, some sort of list of impassable tiles. But it is inherently unwieldy and confusing to work with Object.entries just to get at your impassable tile types.

为什么不将<条码>易读取性<>/条码>界定为一种类型,而不可逾越功能清单则属于这种类型? 从概念上来说,这更符合数据的实际含义。 它还把<代码>目标.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": []
    }
  }
}

现在,你可以很容易地把你无法逾越的手脚、其类型和相关位置数据绘制成图,同时保持全身的推论和安全。 而且,它看着海事组织更加清楚和容易理解。

// 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://codesand Box.io/s/goofy-snyder-u9x60?file=/src/App.tsx” rel=“nofollow noreferer”>https://codesand Box.io/s/goofy-snyder-u9x60?file=/src/App.tsx


你可以进一步将这一理念延伸到你如何构建你的级别及其接口。 为什么没有<条码>水平JSON成为一系列<条码>水平的物体,每个物体都有一个名称和一套碎片?

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://codesand Box.io/s/hopeful-grass-dnohi?file=/src/App.tsx”rel=“nofollow noreferer”>https://codesand Box.io/s/hopeful-gras-dnohi?file=/src/App.tsx

问题回答

这些问题涉及。 为什么不反对。 钥匙将重物归原体中的一种关键类型?。 对这两种情况的答复是,原样中的物体类型不是exact<>em>>(见microsoft/Type#12936);物体类型数值允许在汇编者不知情的情况下得到额外特性。 这使得交接和集体继承成为可能,这非常有用。 但这可能导致混乱。

例如,如果我有价值<代码>。 姓名: string}。 因此,我可以说<代码>。 反对:Array<[“名称”,扼杀]>:

interface NameHaver { name: string }
declare const nameHaver: NameHaver;
const entries: Array<["name", string]> = Object.entries(nameHaver); // error here: why?
entries.map(([k, v]) => v.toUpperCase()); 

姓名 Haver a. 仅限“<> 姓名/编码> 财产,如:

interface NameHaver { name: string }
class Person implements NameHaver { constructor(public name: string, public age: number) { } }
const nameHaver: NameHaver = new Person("Alice", 35);
const entries: Array<["name", string]> = Object.entries(nameHaver); // error here: ohhh
entries.map(([k, v]) => v.toUpperCase());  // explodes at runtime!

Oops。 我们假定<代码>名称 Haver 各项数值总是string,但其中一项为 number,对toUpperCase (表示不高兴。 假设<代码>目标.entries()的生成,唯一安全的东西是Array<[string, known]>(但标准图书馆使用Array<[string, any]>.


因此,我们能够做些什么? 当然,如果你们知道并且绝对相信某一价值只具有汇编者所知道的关键,那么你就可以写上你自己对<条码>的打字,而使用的是<条码/代码>,而需要非常谨慎。 这里可能的打字:

type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T];
function ObjectEntries<T extends object>(t: T): Entries<T>[] {
  return Object.entries(t) as any;
}

The as any is a type assertion that suppresses the normal complaint about Object.entries(). The type Entries<T> is a mapped type that we immediately look up to produce a union of the known entries:

const entries = ObjectEntries(nameHaver);
// const entries: ["name", string][]

这是在<代码>entries前人工写的同类。 如果您使用<条码>,而不是<条码>。 但是,我们确实想到的是,你所挑出的物体没有未知的额外特性。 如果有人在<条码>上添加非<条码>的附加财产,在<条码>上添加“无照产品,那么你可能在操作时遇到问题。


马达加斯加 4. 与代码的游乐联系





相关问题
selected text in iframe

How to get a selected text inside a iframe. I my page i m having a iframe which is editable true. So how can i get the selected text in that iframe.

How to fire event handlers on the link using javascript

I would like to click a link in my page using javascript. I would like to Fire event handlers on the link without navigating. How can this be done? This has to work both in firefox and Internet ...

How to Add script codes before the </body> tag ASP.NET

Heres the problem, In Masterpage, the google analytics code were pasted before the end of body tag. In ASPX page, I need to generate a script (google addItem tracker) using codebehind ClientScript ...

Clipboard access using Javascript - sans Flash?

Is there a reliable way to access the client machine s clipboard using Javascript? I continue to run into permissions issues when attempting to do this. How does Google Docs do this? Do they use ...

javascript debugging question

I have a large javascript which I didn t write but I need to use it and I m slowely going trough it trying to figure out what does it do and how, I m using alert to print out what it does but now I ...

Parsing date like twitter

I ve made a little forum and I want parse the date on newest posts like twitter, you know "posted 40 minutes ago ","posted 1 hour ago"... What s the best way ? Thanx.