ChatGPT解决这个技术问题 Extra ChatGPT

元素隐式具有“任何”类型,因为“字符串”类型的表达式不能用于索引

为 React 项目尝试 TypeScript,但我遇到了这个错误:

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; }'

当我尝试过滤组件中的数组时出现

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

到目前为止,我查看了文章“Indexing objects in TypeScript”(https://dev.to/kingdaro/indexing-objects-in-typescript-1cgi),因为它有类似的错误,但我尝试将索引签名添加到类型 plotTypes,但我仍然得到相同的错误。

我的组件代码:

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;


m
montrealist

这是因为您尝试使用字符串 name 访问 plotOptions 属性。 TypeScript 理解 name 可能有任何值,而不仅仅是来自 plotOptions 的属性名称。因此 TypeScript 需要在 plotOptions 中添加索引签名,因此它知道您可以在 plotOptions 中使用任何属性名称。但我建议更改 name 的类型,因此它只能是 plotOptions 属性之一。

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

现在您将只能使用 plotOptions 中存在的属性名称。

您还必须稍微更改您的代码。

首先将数组分配给某个临时变量,以便 TS 知道数组类型:

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"
    },
    // ...
}

然后过滤:

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

如果您从 API 获取数据并且无法在编译时键入检查道具,则唯一的方法是将索引签名添加到您的 plotOptions

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

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

{[key: string]: boolean} 这很有帮助,非常直观
V
Vikram Deshmukh

对于将来偶然发现此问题的任何人:

如果您收到 TypeScript 错误

'...字符串类型的表达式不能用于索引...'

然后只需指定“字符串类型的表达式”是该对象类型的键。例如,

const someObj:ObjectType = data;
const field = 'username';

// This gives an error
const temp = someObj[field];

// Solution 1: When the type of the object is known
const temp = someObj[field as keyof ObjectType]

// Solution 2: When the type of the object is not known
const temp = someObj[field as keyof typeof someObj]

这显示“类型 'any' 不可分配给类型 'never” 错误
@Franklin'jGil'z 你能分享一个片段吗?如果您有隐式或显式指定的类型,则不应出现该错误。
@VikramDeshmukh 感谢您的编辑,但我删除了我的评论。有大量的边缘情况我没有考虑在内。请参阅有关它的大量讨论:github.com/microsoft/TypeScript/issues/21732 据我所知,这还不是一个已解决的问题
如果您使用不在此对象中的键索引对象 T,则结果值是未定义的。你想要的类型是 T[K] |未定义,而不仅仅是 T[K]。
N
Noel Yap

使用 Object.keys 时,以下工作:

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

出色的!为什么这能解决问题?
@Andru 这解决了这个问题,因为 TS 认识到 keythis 实际拥有的东西。事实上,我们甚至可以去掉类名并使用 this 来代替 this[key as keyof this]
@ChristosLytras 我明白了。遗憾的是 TypeScript 必须通过类型转换来告知这一点。 ..当 Object.keys 的元素循环时,key 实际上还能是什么? - 在未来的 TypeScript 版本中,我猜可能不需要这种类型转换。
A
Alex Mckay
// 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];

不好 - 错误的原因是 object 类型在默认情况下只是一个空对象。因此,不可能使用 string 类型来索引 {}

更好 - 错误消失的原因是因为现在我们告诉编译器 obj 参数将是字符串/值 (string/any) 对的集合。但是,我们使用的是 any 类型,因此我们可以做得更好。

最佳 - T 扩展空对象。 U 扩展了 T 的键。因此 U 将始终存在于 T 上,因此它可以用作查找值。

这是一个完整的例子:

我已经切换了泛型的顺序(U extends keyof T 现在在 T extends object 之前)以强调泛型的顺序并不重要,您应该选择一个对您的函数最有意义的顺序。

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'

替代语法

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

我用这个函数写了一个小 npm package 来让那些不熟悉 Typescript 的人更容易完成这项任务。
打包和缩小后,包大约有 38 个字节。
G
Gennady Magomaev

我用这个:

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

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

注意:“[key:string]”是什么? JavaScript 中的对象主要是由键值对组成的属性集合。此外,键只能是字符串(即使是数组元素),但值可以是任何数据类型。

如果您在对象中使用可选属性:

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

...您应该将“未定义”值添加到 IObjectKeys 接口中:

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

如果我想让某些属性成为可选的?例如 room_id?:number;
不,?optional 这意味着可能是 undefined,所以我应该像这样添加它 [key: string]: string | number | undefined。解决了问题Property 'room_id' of type 'number | undefined' is not assignable to string index type 'string | number'. :)
@GennadyMagomaev您好,我遇到了同样的问题,您的回答对我帮助很大!我不完全理解这意味着什么:“[key:string]:string | number”。这是否意味着对象的键可以是字符串,在“:”之后是否意味着它也可以是字符串或数字或?我很困惑...请您向我解释一下好吗?
最好、最干净、最灵活的解决方案。
如果需要,您也可以扩展 Record,将 any 替换为选项。
O
Onesmus Muna

我已经模拟了这个问题。看起来问题是我们应该如何在 Typescript 中使用括号表示法动态访问对象属性

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

export default class User {
  constructor(private data: IUserProps) {}

  get(propName: string): string | number {
    return this.data[propName as keyof IUserProps];
  }
}

我发现了一个可能有助于更好地理解这一点的博客。

这是一个链接https://www.nadershamma.dev/blog/2019/how-to-access-object-properties-dynamically-using-bracket-notation-in-typescript/


对不起@double-beep,我已经更新了我的帖子。这实际上是我在这里回答问题的第一篇文章。
A
Alonad

当我们执行 obj[key] 之类的操作时,Typescript 无法确定该对象中是否存在该键。我做了什么:

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

A
Andrew Zagarichuk

感谢 Alex Mckay,我决定动态设置道具:

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

M
Moumit

没有 typescript 错误

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

工作正常,但我建议使用 forEach 而不是 map,因为 map 通常会返回一些东西。
N
Nathan Fast

我对 Alex McKay 的功能/用法做了一些小改动,我认为这可以更容易理解它的工作原理,并且还遵守 no-use-before-define 规则。

首先,定义要使用的函数:

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

按照我写的方式,函数的泛型首先列出对象,然后是对象上的属性(这些可以按任何顺序出现,但是如果您在 T extends object 之前指定 U extends key of T,则会破坏 {3 } 规则,而且首先有对象,然后是它的属性才有意义。最后,我使用了更常见的函数语法而不是箭头运算符(=>)。

无论如何,通过这些修改,您可以像这样使用它:

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

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

getKeyValue(user, "name")

再一次,我发现它更具可读性。


S
StepUp

它适用于 keyofas 运算符:

const keys: [keyof ITrainInfo] = Object.keys(this.trainInfo) as [
    keyof ITrainInfo,
]
keys.forEach((property) => {
    // console.log(tmpUser[property])
    if (this.trainInfo === undefined) return
    if (this.trainInfo[property] !== undefined) {
        // your code here
        /*const trainsToSet = trains.find((field) => field.name === property)
        if (trainsToSet != undefined)
            trainsToSet.value = this.trainInfo[property]?.toString()
        */
    }
})

O
O-9

这不是对原始问题的答案,而是针对此问题的通用解决方法。

原始问题:person[cr.field] 导致此错误

我正在做一个通用的高级搜索表单,用户可以在其中选择一个字段、比较器和所需的值。尝试根据键从对象中读取值时,出现此错误(尽管 field 值是字符串类型,我认为应该没问题)

所以我要做的是像这样提取 [key, value]

const x: [string, any] = Object.entries(person).find(([key, _]) => key === cr.field);

例如,如果我的条件 (cr) 是 { field: 'name', value: 'John' } 并且字段 name 实际上存在于 person obj. 中,它应该返回字段名称和值作为元组(x 是 [string, any] 或 undef)。如果未找到,则未定义。


N
Nelcon Croos

公共getUserName():字符串{

const accessToken = this.getAccessToken();
const claims:any = this.getUserClaims();
console.log('access token ',accessToken);
this.getUserInfo();
return claims['sub'].split('@')[0];

}

//给变量任何类型


d
dwjbosman

我知道这有点太晚了,但只需要添加一点类型转换,我编写了一个静态函数,可以安全地返回具有正确类型的键数组。您只需要定义类型并将对象作为参数传递:

export class ObjectUtil {
  public static getObjectKeys<T>(obj: Object) {
    if (!obj) {
      return [];
    }

    return Object.keys(obj).map((key: string) => key as keyof T);
  }
}

下面是一个简单的例子:

ObjectUtil.getObjectKeys<Address>(address).forEach((key) => {
  console.log(address[key]);
});

Z
Zeke

这对我有用。 tsconfig.json 有一个选项 noImplicitAny,它被设置为 true,我只是简单地将它设置为 false,现在我可以使用字符串访问对象中的属性。


这将删除严格,如果我们继续删除这些限制,那么使用 typescript 就没有意义了。
这并不能解决问题,它只是忽略它。
@Zeke 我理解队友 :) 我在匆忙写作。我的意思是,如果我们只是通过告诉它忽略来继续解决问题,那么一开始就没有意义。但话又说回来,一切都取决于项目,以及每个项目的决定。
我更喜欢这个......即使像 c# 这样的强类型语言也有“var”。参考 stackoverflow.com/questions/62377614/… ...有点过度设计,以放置各种颠簸代码来访问一个简单的属性。
C# 中的 var 与 Typescript 中的 any 不同。如果无法推断类型,var 将无法在像 C# 这样的强类型语言中工作,而 any 允许变量随时采用任何类型 - any 变量可以重新分配给完全不同类型的值。这在 C# 中是不可能的。

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅