简体   繁体   English

理解打字稿接口

[英]Understanding typescript interfaces

I am tying to understand format of typescript/javascript and need help in understanding certain steps here.我想了解 typescript/javascript 的格式,并且需要帮助来理解此处的某些步骤。 I am parsing a JSON file into an object and then trying to read values to update some values.我正在将 JSON 文件解析为一个对象,然后尝试读取值以更新某些值。

Thanks to Timmy chan I got some pointers from my previous post here, now building on that.感谢 Timmy chan,我从我之前的帖子中得到了一些建议,现在以此为基础。 Objects creation using interface for JSON types in typescript 使用打字稿中 JSON 类型的接口创建对象

Say I have this interface defined,假设我定义了这个接口,

interface AllData {
  [value:string]:Row 
}

interface Row {
  eDate: string;
  mystate: string;
  mystateCity: string;
  numberofpeople: number;
}

let Data: AllData ;

I read the file like this and assign it to Data variable.我像这样读取文件并将其分配给 Data 变量。

 const response = await fetchFile(fileName);
    Data= await response.json();

Now I want to create a new object which will only have rows现在我想创建一个只有行的新对象

 const CountData: Row[] = {} // this gives error Type '{}' is missing the following properties from type 'CountyGraphRow[]': length, pop, push,
 
if I change it to 
const CountData: Row[] = []  // when to have {} or [].. What is the difference?

  for (const rowData in Data["value"]) 
  {
    console.log(rowData); //  this is coming out 0
    CountData.push({  //  error at TypeError: Cannot read property 'eDate' of undefined
      eDate: Data[rowData].eDate,  // Something is wrong in the way I am accessing them.
      mystate: Data[rowData].mystate,
      mystateCity: Data[rowData].mystateCity,
      numberofpeople: Data[rowData].numberofpeople > 20? 20 : Data[rowData].numberofpeople < 5? 5: Data[rowData].numberofpeople,
    })
  }

Here is how the file looks like这是文件的样子

{
  "value": [
    {
      "eDate": "2020-03-01T00:00:00.000Z",
      "mystate": "state1",
      "mystateCity": "state1, ID",
      "numberofpeople": 2.973529602
    },
    {
      "eDate": "2020-03-02T00:00:00.000Z",
      "mystate": "state1",
      "mystateCity": "state1, ID",
      "numberofpeople": 2.973529602
    },
    {
      "eDate": "2020-03-03T00:00:00.000Z",
      "mystate": "state1",
      "mystateCity": "state1, ID",
      "numberofpeople": 2.973529602
    }
]}

--------------Update 2------------- --------------更新2------------

interface AllData {
    [value: string]: Row
}

interface Row {
    eDate: string;
    mystate: string;
    mystateCity: string;
    numberofpeople: number;
}

let Data: AllData;

Data = JSON.parse(`{ 
  "value": [
    {
      "eDate": "2020-03-01T00:00:00.000Z",
      "mystate": "state1",
      "mystateCity": "state1, ID",
      "numberofpeople": 2.973529602
    },
    {
      "eDate": "2020-03-02T00:00:00.000Z",
      "mystate": "state1",
      "mystateCity": "state1, ID",
      "numberofpeople": 2.973529602
    },
    {
      "eDate": "2020-03-03T00:00:00.000Z",
      "mystate": "state1",
      "mystateCity": "state1, ID",
      "numberofpeople": 2.973529602
    }
]}`);

const CountData: Row[] = [];

for (const rowData in Data["value"]) { // Data["value"] I am assuming this //will pick all objects   "value": [
   // {
     // "eDate": "2020-03-01T00:00:00.000Z",
// please correct me if I am wrong

   

 console.log(rowData); //  this is coming out 0
    CountData.push({  //  error at TypeError: Cannot read property 'eDate' of undefined
        eDate: Data[rowData].eDate,  // Something is wrong in the way I am accessing them.
        mystate: Data[rowData].mystate,
        mystateCity: Data[rowData].mystateCity,
        numberofpeople: Data[rowData].numberofpeople > 20 ? 20 : Data[rowData].numberofpeople < 5 ? 5 : Data[rowData].numberofpeople,
    })
}

Oh, the issue with {} vs [] : the former is an empty object and the latter is an empty array.哦, {}[] :前者是一个空对象,后者是一个空数组。 In JavaScript, arrays are all objects but not all objects are arrays.在 JavaScript 中,数组都是对象,但并非所有对象都是数组。 Specifically, arrays have all the array methods like push() that you care about.具体来说,数组具有您关心的所有数组方法,例如push() If CountData is supposed to be an array, you can't assign {} to it, because it lacks the things arrays need.如果CountData应该是一个数组,则不能为其分配{} ,因为它缺少数组所需的东西。

Moving on the main part of your question:继续你的问题的主要部分:


The first problem here is that your interface这里的第一个问题是你的界面

interface AllData {
  [value:string]:Row 
}

has a string index signature , which means "an AllData can have any number of keys of any string value whatsoever, and the property at each of those keys will be a single Row object".有一个字符串索引签名,这意味着“一个AllData可以有任意数量的任意string值的键,并且每个键的属性将是一个Row对象”。

Please note that the value in the above definition is just a placeholder and has no effect whatsoever on the type.请注意,上述定义中的value只是一个占位符,对类型没有任何影响。 It is the same exact definition as它与完全相同的定义

interface AllData {
  [x:string]:Row 
}

Anyway, your problem is that the Data you are reading does not have that shape.无论如何,您的问题是您正在阅读的Data没有那种形状。 It has a single key of exactly the name "value" , and the property at that key is an array of Row objects.它具有完全相同的名称的单个"value" ,并在该键的属性是一个数组Row对象。 Meaning the following:含义如下:

let Data: { value: Row[] };

Once you fix that, let's look at the smallest possible change to your code to get it working, and then improve from there:一旦你解决了这个问题,让我们看看对你的代码进行最小的更改以使其正常工作,然后从那里进行改进:

const CountData: Row[] = [];
for (const rowData in Data["value"]) {
    CountData.push({
        eDate: Data["value"][rowData].eDate,
        mystate: Data["value"][rowData].mystate,
        mystateCity: Data["value"][rowData].mystateCity,
        numberofpeople: Data["value"][rowData].numberofpeople > 20 ? 20 :
          Data["value"][rowData].numberofpeople < 5 ? 5 : 
          Data["value"][rowData].numberofpeople,
    })
}

The loop for (const rowData in Data["value"]) iterates over the keys of the Data["value"] array.for (const rowData in Data["value"])在所述的迭代Data["value"]数组。 These are going to be the string values corresponding to the array indices: "0" , "1" , "2" , etc. It is generally not recommended to iterate over arrays via for..in , because you'll get the indices as strings, possibly out of order, and with possible other unexpected things in there.这些将是与数组索引对应的字符串值: "0""1""2"等。通常不建议通过for..in迭代数组,因为您将获得索引作为字符串,可能是乱序的,并且可能有其他意想不到的东西。 Still, it sort of works.不过,它有点工作。

The main issue here, though: since you are iterating over Data["value"] 's keys, you need to read its properties by indexing into Data["value"] , not Data .不过,这里的主要问题是:由于您正在迭代Data["value"]的键,您需要通过索引到Data["value"]而不是Data来读取其属性。


Let's stop iterating over array indices with for..of and use a regular for loop:让我们停止使用for..of迭代数组索引并使用常规 for 循环:

const CountData: Row[] = [];
for (let i = 0; i < Data["value"].length; i++) {
    CountData.push({
        eDate: Data["value"][i].eDate,
        mystate: Data["value"][i].mystate,
        mystateCity: Data["value"][i].mystateCity,
        numberofpeople: Data["value"][i].numberofpeople > 20 ? 20 : 
          Data["value"][i].numberofpeople < 5 ? 5 : Data["value"][i].numberofpeople,
    })
}

That is now guaranteed to happen in order and without any weird non-numeric indices.现在保证按顺序发生并且没有任何奇怪的非数字索引。


Next improvements: let's stop saying Data["value"] since there's no reason to use bracket property access with a string literal that's a valid JavaScript identifier.下一步改进:让我们停止说Data["value"]因为没有理由使用括号属性访问和字符串文字是有效的 JavaScript 标识符。 You can just use Data.value .您可以只使用Data.value

Also, instead of writing Data.value[i] a bunch of times, just save it to its own variable (hey we can call that rowData now):此外,不要多次写入Data.value[i] ,只需将其保存到自己的变量中(嘿,我们现在可以调用该rowData ):

const CountData: Row[] = [];
for (let i = 0; i < Data.value.length; i++) {
    const rowData = Data.value[i]; 
    CountData.push({
        eDate: rowData.eDate,
        mystate: rowData.mystate,
        mystateCity: rowData.mystateCity,
        numberofpeople: rowData.numberofpeople > 20 ? 20 : 
          rowData.numberofpeople < 5 ? 5 : rowData.numberofpeople,
    })
}

Next, you might consider using a for...of loop to directly iterate over the rowData element s without having to care about their numeric indices :接下来,您可以考虑使用for...of循环直接迭代rowData元素,而不必关心它们的数字索引

const CountData: Row[] = [];
for (const rowData of Data.value) { 
    CountData.push({
        eDate: rowData.eDate,
        mystate: rowData.mystate,
        mystateCity: rowData.mystateCity,
        numberofpeople: rowData.numberofpeople > 20 ? 20 : 
          rowData.numberofpeople < 5 ? 5 : 
          rowData.numberofpeople,
    });
}

} }


Next, copying individual properties is fine, but if you're going to copy all or most of the properties, you can use object literal spread syntax to do it quickly (or Object.assign() which is similar) and then just rewrite the one property you want to change:接下来,复制单个属性很好,但是如果您要复制所有或大部分属性,您可以使用对象字面量扩展语法来快速完成(或Object.assign()类似),然后只需重写您要更改的一项属性:

const CountData: Row[] = [];
for (const rowData of Data.value) {
    CountData.push({ 
        ...rowData,
        numberofpeople: rowData.numberofpeople > 20 ? 20 : 
          rowData.numberofpeople < 5 ? 5 : rowData.numberofpeople
    })
}

} }


Finally, if all you're doing is walking over one array and producing another array of the exact same size with each element in the input producing a single element in the output, you can dispense with looping and push ing entirely and use Array.prototype.map() instead:最后,如果您所做的只是遍历一个数组并生成另一个大小完全相同的数组,其中输入中的每个元素在输出中生成一个元素,则您可以完全省去循环和push并使用Array.prototype.map()代替:

const CountData = Data.value.map(rowData => ({
    ...rowData,
    numberofpeople: rowData.numberofpeople > 20 ? 20 : 
      rowData.numberofpeople < 5 ? 5 : rowData.numberofpeople
}));

} }


Whew!哇! I think that's as good as I can make it.我认为这是我能做到的。

Playground link to code Playground 链接到代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM