我总是用--noImplicitAny标志来编译Typescript。这是有道理的,因为我希望我的类型检查尽可能严格。

我的问题是,使用以下代码,我收到错误Index signature of object type implicitly has an 'any' type

interface ISomeObject {
    firstKey:   string;
    secondKey:  string;
    thirdKey:   string;
}

let someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue'
};

let key: string = 'secondKey';

let secondValue: string = someObject[key];


要注意的是,这个想法是key变量来自应用程序中的其他地方,并且可以是对象中的任何key。

我尝试过显式地转换键入:

let secondValue: string = <string>someObject[key];


或者我的场景无法用--noImplicitAny吗?

#1 楼

添加索引签名将使TypeScript知道类型是什么。

在您的情况下为[key: string]: string;

interface ISomeObject {
    firstKey:      string;
    secondKey:     string;
    thirdKey:      string;
    [key: string]: string;
}


但是,这也强制所有属性类型匹配索引签名。由于所有属性都是string,因此可以正常工作。


尽管索引签名是描述数组和“字典”模式的强大方法,但它们还强制所有属性与其返回类型匹配。


编辑:

如果类型不匹配,则可以使用联合类型[key: string]: string|IOtherObject;

对于联合类型,最好让TypeScript推断类型而不是定义类型。

// Type of `secondValue` is `string|IOtherObject`
let secondValue = someObject[key];
// Type of `foo` is `string`
let foo = secondValue + '';


如果索引签名中有很多不同的类型,可能会有些混乱。替代方法是在签名中使用any[key: string]: any;然后,您需要像上面一样转换类型。

评论


并且如果您的接口看起来像接口ISomeObject {firstKey:string; secondKey:IOtherObject;我猜这不可能吗?

–贾斯珀·舒尔特(Jasper Schulte)
2015年10月6日,11:46



谢谢!任何类型的组合以及每种情况下的类型转换似乎是一种行之有效的方法。

–贾斯珀·舒尔特(Jasper Schulte)
2015年10月6日,12:03

嗨,如何处理“ anyObject [key:Object] ['name']”?

– Code_Crash
19/09/16在11:23

或者说类似_obj = {}的话;让_dbKey = _props [key] ['name']; _obj [_dbKey] = this [key];这里_props是object,object [key]还将返回一个具有name属性的对象。

– Code_Crash
19-09-16在11:59

因此,缩小键的解决方案是更改对象的界面!这不是世界颠倒了吗?

–狂野
20-10-21在6:26

#2 楼

避免错误的另一种方法是像这样使用强制转换:

let secondValue: string = (<any>someObject)[key];
(请注意括号)

唯一的问题是这不是类型-safe了,因为您要强制转换为any。但是您总是可以转换回正确的类型。

ps:我使用的是打字稿1.7,不确定以前的版本。

评论


为了避免tslint警告,您还可以使用:let secondValue:string =(someObject as any)[key];

–briosheje
17年12月6日在9:57

@briosheje这很有用:)

–内森(Nathan)
20 Nov 12'上午0:47

#3 楼

TypeScript 2.1引入了解决此问题的简便方法。

const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];


我们可以在编译阶段通过keyof关键字访问所有对象属性名称(请参见changelog)。

只需要用string替换keyof ISomeObject变量类型。
现在编译器知道key变量只允许包含ISomeObject的属性名称。

完整示例:

interface ISomeObject {
    firstKey:   string;
    secondKey:  string;
    thirdKey:   number;
}

const someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   3
};

const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];

// You can mix types in interface, keyof will know which types you refer to.
const keyNumber: (keyof ISomeObject) = 'thirdKey';
const numberValue: number = someObject[keyNumber];


typescriptlang.org上的实时代码(设置noImplicitAny选项)

进一步阅读keyof的更多用法。

评论


但是,如果我们将键声明为const key =(key of ISomeObject)='second'+'Key',它将无法正常工作

–失望
18年7月9日在11:34

#4 楼

以下tsconfig设置将使您可以忽略这些错误-将其设置为true。


excludeImplicitAnyIndexErrors

抑制noImplicitAny索引缺少索引签名的对象的错误。 />

评论


那是您不应该做的事情-可能是您团队中的某人已经明确设置了此编译器选项,以使代码更加安全!

–atsu85
16年9月1日在15:04

我不同意此选项的确切含义:使用--noImplicitAny允许使用括号表示法。完全匹配op的问题。

– Ghetolay
16年11月8日在16:58

我同意@Ghetolay。如果无法修改接口,这也是唯一的选择。例如,使用XMLHttpRequest之类的内部接口。

–马可·罗伊(Marco Roy)
16 Dec 9'在20:36



我也同意@Ghetolay。我很好奇这与Pedro Villa Verde的回答在质量上有何不同(除了代码不太丑的事实)。我们都知道,应尽可能避免使用字符串访问对象属性,但有时我们会在理解风险的同时享受自由。

–斯蒂芬·保罗(Stephen Paul)
17年11月17日在6:56



这只是权衡。选择您喜欢的对象:更少的错误表面积和严格的索引访问权限,或者更大的错误表面积并轻松访问未知索引。 TS2.1 keyof运算符可以帮助保持所有内容严格,请参阅Piotr的答案!

–trusktr
18/12/21在19:24



#5 楼

上述解决方案的“关键”起作用。但是,如果变量仅使用一次(例如遍历对象等),则也可以类型转换为整数。

for (const key in someObject) {
    sampleObject[key] = someObject[key as keyof ISomeObject];
}


评论


谢谢。当迭代另一个对象的键时,这适用于任意键访问。

–布卡贝
19年3月12日在12:18

#6 楼

使用keyof typeof

 const cat = {
    name: 'tuntun'
}

const key: string = 'name' 

cat[key as keyof typeof cat]
 


评论


就像魅力。请说明typeof keyof的用法

–深入
20年9月8日在6:39

#7 楼

没有索引器?然后自己动手!
我已将其全局定义为定义对象签名的简便方法。如果需要,T可以是any
type Indexer<T> = { [ key: string ]: T };

我只是将indexer添加为类成员。
indexer = this as unknown as Indexer<Fruit>;

所以我最终得到了这个结果:
constructor(private breakpointResponsiveService: FeatureBoxBreakpointResponsiveService) {

}

apple: Fruit<string>;
pear: Fruit<string>;

// just a reference to 'this' at runtime
indexer = this as unknown as Indexer<Fruit>;

something() {

    this.indexer['apple'] = ...    // typed as Fruit

这样做的好处是可以找回正确的类型-使用<any>的许多解决方案都会为您丢失键入内容。请记住,这不会执行任何运行时验证。如果您不确定某些事物是否存在,仍然需要检查是否存在某些事物。
如果您要过于谨慎,并且使用的是strict,则可以执行此操作以显示可能的所有位置需要做一个明确的未定义检查:
type OptionalIndexed<T> = { [ key: string ]: T | undefined };

我通常不认为这是必要的,因为如果我从某个地方获得字符串属性,我通常知道它是有效的。
我已经发现如果我有很多代码需要访问索引器,并且键入可以在一个地方更改,则此方法特别有用。
注意:我使用的是strict模式,绝对需要unknown
编译后的代码仅为indexer = this,因此它与打字稿为您创建_this = this时非常相似。

评论


在某些情况下,您可能可以改用Record 类型-我现在无法研究其详细信息,但在某些有限的情况下,它可能会更好。

–Simon_Weaver
19年8月5日在22:45

#8 楼

这样声明对象。

export interface Thread {
    id:number;
    messageIds: number[];
    participants: {
        [key:number]: number
    };
}


#9 楼

与@Piotr Lewandowski的答案类似,但在forEach中:

const config: MyConfig = { ... };

Object.keys(config)
  .forEach((key: keyof MyConfig) => {
    if (config[key]) {
      // ...
    }
  });


评论


您是如何使它工作的?我正在尝试相同的操作(ts 3.8.3),尽管它抛出错误说:类型'(field:“ id” |“ url” |“ name”)=> void'的参数不能分配给类型'((值:字符串,索引:数字,数组:字符串[])=>无效'。我的代码看起来像是Object.keys(components).forEach((comp:Component)=> {...},其中Component是一种类型(如MyConfig)。

–Girrafish
20-4-6的16:46

#10 楼

创建一个接口来定义“索引器”接口
,然后使用该索引创建对象。
注意:在执行每个项目的类型方面,其他答案也将描述同样的问题,但这就是通常正是您想要的。
您可以根据需要设置通用类型参数:ObjectIndexer< Dog | Cat>
// this should be global somewhere, or you may already be 
// using a library that provides such a type
export interface ObjectIndexer<T> {
  [id: string]: T;
}

interface ISomeObject extends ObjectIndexer<string>
{
    firstKey:   string;
    secondKey:  string;
    thirdKey:   string;
}

let someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue'
};

let key: string = 'secondKey';

let secondValue: string = someObject[key];

Typescript游乐场

您甚至可以在通用约束中使用它定义通用类型时:
export class SmartFormGroup<T extends IndexableObject<any>> extends FormGroup
然后可以在类中索引T :-)

评论


我认为字典没有标准的“内置”接口来表示{[key:string]:T},但是如果有的话,请编辑此问题以删除我的ObjectIndexer。

–Simon_Weaver
19年1月23日在2:11

#11 楼

声明其键为字符串和值的类型可以为任意
,然后声明此类型的对象,并且皮棉不会显示

type MyType = {[key: string]: any};


所以您的代码将是

type ISomeType = {[key: string]: any};

    let someObject: ISomeType = {
        firstKey:   'firstValue',
        secondKey:  'secondValue',
        thirdKey:   'thirdValue'
    };

    let key: string = 'secondKey';

    let secondValue: string = someObject[key];


#12 楼

在今天,更好的解决方案是声明类型。像

enum SomeObjectKeys {
    firstKey = 'firstKey',
    secondKey = 'secondKey',
    thirdKey = 'thirdKey',
}

let someObject: Record<SomeObjectKeys, string> = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue',
};

let key: SomeObjectKeys = 'secondKey';

let secondValue: string = someObject[key];


#13 楼

我可以通过3个步骤使用Typescript 3.1找到的最简单的解决方案是:

1)创建接口

interface IOriginal {
    original: { [key: string]: any }
}


2)创建类型化的副本

let copy: IOriginal = (original as any)[key];


3)在任何地方使用(包括JSX)

<input customProp={copy} />


#14 楼

我有两个接口。首先是其他的孩子。我做了以下操作:

在父接口中添加了索引签名。
使用as关键字使用了适当的类型。

完整的代码如下:
子接口:
interface UVAmount {
  amount: number;
  price: number;
  quantity: number;
};

父接口:
interface UVItem  {
// This is index signature which compiler is complaining about.
// Here we are mentioning key will string and value will any of the types mentioned.
  [key: string]:  UVAmount | string | number | object;

  name: string;
  initial: UVAmount;
  rating: number;
  others: object;
};

反应组件:
let valueType = 'initial';

function getTotal(item: UVItem) {
// as keyword is the dealbreaker.
// If you don't use it, it will take string type by default and show errors.
  let itemValue = item[valueType] as UVAmount;

  return itemValue.price * itemValue.quantity;
}



#15 楼

无需使用ObjectIndexer<T>或更改原始对象的界面(如大多数其他答案中所建议的那样)。
您可以使用以下命令将键的选项范围缩小为字符串类型following:
type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];

这个很棒的解决方案来自这里一个相关问题的答案。
就像您在T内缩小容纳V值的键一样。因此,如果要限制为字符串,您可以这样做:
type KeysMatching<ISomeObject, string>;

在您的示例中:
interface ISomeObject {
    firstKey:   string;
    secondKey:  string;
    thirdKey:   string;
}

let someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue'
};

let key: KeysMatching<SomeObject, string> = 'secondKey';

// secondValue narrowed to string    
let secondValue = someObject[key];

优点是您的ISomeObject现在甚至可以容纳混合类型,并且仍然可以将键范围缩小到仅字符串值,其他值类型的键将被视为无效。要说明:
interface ISomeObject {
    firstKey:   string;
    secondKey:  string;
    thirdKey:   string;
    fourthKey:  boolean;
}

let someObject: ISomeObject = {
    firstKey:   'firstValue',
    secondKey:  'secondValue',
    thirdKey:   'thirdValue'
    fourthKey:   true
};


// Type '"fourthKey"' is not assignable to type 'KeysMatching<ISomeObject, string>'.(2322)
let otherKey: KeysMatching<SomeOtherObject, string> = 'fourthKey';

let fourthValue = someOtherObject[otherKey];

您可以在这个游乐场找到这个示例。