Öğe örtük olarak 'herhangi' bir türe sahiptir çünkü 'dizge' türünün ifadesi dizin oluşturmak için kullanılamaz


98

Bir React projesi için TypeScript'i deniyorum ve bu hataya takılı kaldım:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ train_1: boolean; train_2: boolean; train_3: boolean; train_4: boolean; }'.
  No index signature with a parameter of type 'string' was found on type '{ train_1: boolean; train_2: boolean; train_3: boolean; train_4: boolean; }'

Bileşenimdeki diziyi filtrelemeye çalıştığımda görünen

.filter(({ name }) => plotOptions[name]);

Şimdiye kadar benzer bir hata olduğu için "TypeScript'te nesneleri indeksleme" ( https://dev.to/kingdaro/indexing-objects-in-typescript-1cgi ) makalesine baktım , ancak indeks imzasını eklemeye çalıştım. yazın plotTypesve hala aynı hatayı alıyorum.

Bileşen kodum:

import React, { Component } from "react";
import createPlotlyComponent from "react-plotly.js/factory";
import Plotly from "plotly.js-basic-dist";
const Plot = createPlotlyComponent(Plotly);

interface IProps {
  data: any;
}

interface IState {
  [key: string]: plotTypes;
  plotOptions: plotTypes;
}

type plotTypes = {
  [key: string]: boolean;
  train_1: boolean;
  train_2: boolean;
  train_3: boolean;
  train_4: boolean;
};

interface trainInfo {
  name: string;
  x: Array<number>;
  y: Array<number>;
  type: string;
  mode: string;
}

class FiltrationPlots extends Component<IProps, IState> {
  readonly state = {
    plotOptions: {
      train_1: true,
      train_2: true,
      train_3: true,
      train_4: true
    }
  };
  render() {
    const { data } = this.props;
    const { plotOptions } = this.state;

    if (data.filtrationData) {
      const plotData: Array<trainInfo> = [
        {
          name: "train_1",
          x: data.filtrationData.map((i: any) => i["1-CumVol"]),
          y: data.filtrationData.map((i: any) => i["1-PressureA"]),
          type: "scatter",
          mode: "lines"
        },
        {
          name: "train_2",
          x: data.filtrationData.map((i: any) => i["2-CumVol"]),
          y: data.filtrationData.map((i: any) => i["2-PressureA"]),
          type: "scatter",
          mode: "lines"
        },
        {
          name: "train_3",
          x: data.filtrationData.map((i: any) => i["3-CumVol"]),
          y: data.filtrationData.map((i: any) => i["3-PressureA"]),
          type: "scatter",
          mode: "lines"
        },
        {
          name: "train_4",
          x: data.filtrationData.map((i: any) => i["4-CumVol"]),
          y: data.filtrationData.map((i: any) => i["4-PressureA"]),
          type: "scatter",
          mode: "lines"
        }
      ].filter(({ name }) => plotOptions[name]);
      return (
        <Plot
          data={plotData}
          layout={{ width: 1000, height: 1000, title: "A Fancy Plot" }}
        />
      );
    } else {
      return <h1>No Data Loaded</h1>;
    }
  }
}

export default FiltrationPlots;

Yanıtlar:


100

Bu plotOptions, dizeyi kullanarak özelliğe erişmeye çalıştığınız için olur name. TypeScript name, yalnızca özellik adının değil, herhangi bir değere sahip olabileceğini anlar plotOptions. Bu nedenle TypeScript, dizin imzası eklemeyi gerektirir plotOptions, böylece içinde herhangi bir özellik adını kullanabileceğinizi bilir plotOptions. Ancak name, türünün değiştirilmesini öneririm , böylece yalnızca plotOptionsözelliklerden biri olabilir .

interface trainInfo {
    name: keyof typeof plotOptions;
    x: Array<number>;
    y: Array<number>;
    type: string;
    mode: string;
}

Artık yalnızca içinde bulunan özellik adlarını kullanabileceksiniz plotOptions.

Ayrıca kodunuzu biraz değiştirmelisiniz.

Önce diziyi temp değişkenine atayın, böylece TS dizi türünü bilir:

const plotDataTemp: Array<trainInfo> = [
    {
      name: "train_1",
      x: data.filtrationData.map((i: any) => i["1-CumVol"]),
      y: data.filtrationData.map((i: any) => i["1-PressureA"]),
      type: "scatter",
      mode: "lines"
    },
    // ...
}

Ardından filtreleyin:

const plotData = plotDataTemp.filter(({ name }) => plotOptions[name]);

API'den veri alıyorsanız ve derleme zamanında check props yazmanın bir yolu yoksa, tek yol şunlara dizin imzası eklemektir plotOptions:

type tplotOptions = {
    [key: string]: boolean
}

const plotOptions: tplotOptions = {
    train_1: true,
    train_2: true,
    train_3: true,
    train_4: true
}

34
// bad
const _getKeyValue = (key: string) => (obj: object) => obj[key];

// better
const _getKeyValue_ = (key: string) => (obj: Record<string, any>) => obj[key];

// best
const getKeyValue = <T extends object, U extends keyof T>(key: U) => (obj: T) =>
  obj[key];

Kötü - hatanın nedeni, objecttürün varsayılan olarak yalnızca boş bir nesnedir. Bu nedenle stringindekslemek için bir tür kullanmak mümkün değildir {}.

Daha iyisi - hatanın kaybolmasının nedeni, şimdi derleyiciye objargümanın bir dizi / değer ( string/any) çiftleri koleksiyonu olacağını söylememizdir . Ancak, kullanıyoruzany daha iyisini yapabilmemiz türü .

En iyi - Tboş nesneyi genişletir. Uanahtarlarını genişletir T. Bu nedenle Uher zaman açık olacaktır T, bu nedenle arama değeri olarak kullanılabilir.

İşte tam bir örnek:

Jeneriklerin sırasını değiştirdim ( U extends keyof Tşimdi daha önce geliyor T extends object) jenerik sırasının önemli olmadığını vurgulamak için ve işleviniz için en mantıklı olan sıralamayı seçmelisiniz.

const getKeyValue = <U extends keyof T, T extends object>(key: U) => (obj: T) =>
  obj[key];

interface User {
  name: string;
  age: number;
}

const user: User = {
  name: "John Smith",
  age: 20
};

const getUserName = getKeyValue<keyof User, User>("name")(user);

// => 'John Smith'

Alternatif Sözdizimi

const getKeyValue = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key];

1
Typecript'te yeni olanlar için bu görevi kolaylaştırmak için bu işlevi içeren küçük bir npm paketi yazdım .
Alex Mckay

Paket, paketlenip küçültüldüğünde yaklaşık 38 bayttır.
Alex Mckay

2
Alex, görünüşe göre en kötüden en iyiye ölçeğin, kısa ve basitten prolix sembollü salataya kadar bir ölçeğe yaklaşıyor gibi görünüyor, sadece bir mazoşistin sevebileceği. Bu ruhla, işte sizin "en iyi" sürümünüzün çoğaltılmış ve karıştırılmış "en iyi" sürümü:d ee((oent V[jkkncnt=kfay u skk==e>Ugs(< :ds b]uUeT eKne e=x>:dTy: lc,n=b;T=by>o[x oo<U yc ,t Ketgt )c)yo oe eTV aej ; Txb )t> se de esytkjeeUe otj]b j o:oebe)ytlT(eejfs>toyn> x
webb

10

Bunu kullanıyorum:

interface IObjectKeys {
  [key: string]: string | number;
}

interface IDevice extends IObjectKeys {
  id: number;
  room_id: number;
  name: string;
  type: string;
  description: string;
}

8

Kullanırken Object.keysaşağıdakiler çalışır:

Object.keys(this)
    .forEach(key => {
      console.log(this[key as keyof MyClass]);
    });

4

Böyle bir şey yaptığımızda, obj [key] Typescript, o anahtarın o nesnede var olup olmadığını kesin olarak bilemez. Ben ne yaptım:

Object.entries(data).forEach(item => {
    formData.append(item[0], item[1]);
});

0

Dışarı ile typescripthata

    const formData = new FormData();
    Object.keys(newCategory).map((k,i)=>{  
        var d =Object.values(newCategory)[i];
        formData.append(k,d) 
    })

0

Alex Mckay sayesinde, dinamik bir sahne ayarı yapma kararlılığım vardı:

  for(let prop in filter)
      (state.filter as Record<string, any>)[prop] = filter[prop];

0

Alex McKay'in işlevinde / kullanımında, neden çalıştığını takip etmeyi biraz daha kolaylaştıracağını düşündüğüm ve aynı zamanda kullanmadan önce tanımla kuralına bağlı kalan küçük değişiklikler yaptım .

Öncelikle, kullanmak için bu işlevi tanımlayın:

const getKeyValue = function<T extends object, U extends keyof T> (obj: T, key: U) { return obj[key] }

Bunu yazdım arada, nesne ilk fonksiyon listeleri için jenerik olarak, daha sonra nesne saniye (bunlar herhangi bir sırada ortaya çıkabilir, ancak belirtirseniz mülkiyet U extends key of Tönce T extends objectsen kırmak no-use-before-definekuralı, hem de sadece mantıklı önce nesneye ve ikinci özelliğe sahip olmak için. Son olarak, ok operatörleri ( =>) yerine daha yaygın işlev sözdizimini kullandım .

Her neyse, bu değişikliklerle bunu şu şekilde kullanabilirsiniz:

interface User {
  name: string;
  age: number;
}

const user: User = {
  name: "John Smith",
  age: 20
};

getKeyValue(user, "name")

Ki bu da biraz daha okunaklı buluyorum.


-11

Bu benim için işe yaradı. tsconfig.jsonBir seçenek vardır noImplicitAnyo ayarlı olduğunu true, sadece basit olarak ayarlayın falseve şimdi dizeleri kullanarak nesneleri özelliklerine erişebilirsiniz.


7
Bu katıları kaldıracaktır, o zaman bu kısıtlamaları kaldırmaya devam edersek typcript kullanmanın bir anlamı yoktur.
Joseph Briggs

Katılmıyorum @JosephBriggs. Typescript tabloya birçok başka faydalar getiriyor. Tür kontrolü bunlardan biridir. Projenizin gereksinimlerine bağlı olarak dahil olabilmeniz veya ayrılabilmeniz güzel.
Zeke

15
Bu sorunu çözmez, sadece görmezden gelir.
Perşembe17

1
@Zeke Anlıyorum dostum :) Acelem vardı. Demek istediğim, sorunları sadece görmezden gelmesini söyleyerek çözmeye devam edersek, ilk etapta bunun bir anlamı kalmaz. ancak yine de hepsi projeye ve proje başına alınan kararlara bağlıdır.
Joseph Briggs

Bunu tercih ederim ... c # have "var" gibi güçlü bir yazı dili bile. Stackoverflow.com/questions/62377614/… referanslanıyor ... Basit bir özelliğe erişmek için her türden çarpma kodunu yerleştirmek için biraz daha fazla tasarlandı.
Eric
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.