如何防止出现“对象类型的索引签名隐含有一个'any'”type"当编译typescript noImplicitAny标志启用?

我总是用--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];

需要注意的是,这里的思想是键变量来自应用程序中的其他地方,可以是对象中的任何键。

我已经尝试通过以下方式显式地转换类型:

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

或者我的场景只是不可能与--noImplicitAny?

265139 次浏览

添加索引签名将让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。然后你需要像上面那样强制转换类型。

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

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

唯一的问题是,当你强制转换到any时,这不再是类型安全的。但是您总是可以转换回正确的类型。

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

下面的tsconfig设置将允许您忽略这些错误-将其设置为true。

suppressImplicitAnyIndexErrors

Suppress noimplicit任何索引对象缺乏索引签名的错误。

像这样声明对象。

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

打字稿2.1引入了一种优雅的方法来处理这个问题。

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

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

你只需要用keyof ISomeObject替换string变量类型。 现在编译器知道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用法

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

const config: MyConfig = { ... };


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

上面提到的“keyof”解决方案是有效的。但如果变量只使用一次,例如循环遍历一个对象等,你也可以对它进行类型转换。

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

目前更好的解决方案是声明类型。就像

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];

我可以在3个步骤中使用打字稿3.1找到的最简单的解决方案是:

1)制作界面

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

2)打印一份

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

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

<input customProp={copy} />

创建一个接口来定义'indexer'接口

然后用该索引创建对象。

注意:这仍然会有相同的问题,其他答案已经描述了关于强制每个项目的类型-但这往往正是你想要的。

你可以根据需要设置泛型类型参数: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];

<一个href = " http://www.typescriptlang.org/play/ src =出口% 20接口% 20 objectindexer % 3 ct % 3 e % 20% 7 b % d % 0 a % 20% 20% 5投标% % 20字符串% 5 d % 3 % 20 t % 3 b % d % 0 a % 7 d % 0 d % 0 a % d % 0 ainterface % 20 isomeobject % 20延伸% 20 objectindexer % 3装运箱% 3 e % d % 0 0 % 7 b % d % 0 a % 20 firstkey % 3 a % 20% 20% 20% 20% 20% 20字符串% 3 b % 0 d % 0 a % 20 secondkey % 3 a % 20% 20% 20% 20% 20字符串% 3 b % 0 d % 0 a % 20 thirdkey % 3 a % 20% 20% 20% 20% 20% 20字符串% 3 b % d % 0 a % 7 d % 0 d % 0 a % d % 0我们% 20 someobject % 3 3 d % % 20 isomeobject % 20% 20% 7 b % d % 0 a % 20 firstkey % 3 a % 20% 20% 20% 20% 20% 20% 27 firstvalue % 27% 2 c % d % 0 20% % 20%20%20secondKey%3A%20%20%27secondValue%27%2C%0D%0A%20%20% 20thirdKey%3A%20%20% 20thirdvalue %27%0D%0A%7D%3B%0D%0A%0D%0Alet%20key%3A%20string%20%3D%20%27secondKey%27%3B%0D%0A%0D%0Alet%20secondValue%3A%20string%20%3D%20someObject%5Bkey%5D%3B%0D% 0D% 0D% 0D%0A" rel="noreferrer">Typescript Playground


你甚至可以在定义泛型类型时在泛型约束中使用它:

export class SmartFormGroup<T extends IndexableObject<any>> extends FormGroup

然后类中的T可以被索引:-)

没有索引器?那就自己做吧!

我将其全局定义为定义对象签名的一种简单方法。如果需要,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,所以它非常类似于typescript为你创建_this = this

使用keyof typeof

const cat = {
name: 'tuntun'
}


const key: string = 'name'


cat[key as keyof typeof cat]

声明类型,键为字符串,值为任意 然后用这个类型声明对象,并且lint不会显示

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];

我有两个界面。第一个是别人的孩子。我做了以下几点:

  1. 父接口增加索引签名。
  2. 使用适当的类型使用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;
}


不需要使用ObjectIndexer<T>,或者改变原始对象的接口(就像在大多数其他答案中建议的那样)。 您可以简单地将key选项缩小为字符串类型的选项,使用以下命令:

type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];

这个伟大的解决方案来自这里有一个相关问题的答案

就像你缩小到T里面包含V值的键。所以在你的例子中,to to limit to string你会这样做:

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];

你会发现这个例子<一个href = " https://www.typescriptlang.org/play?代码/ C4TwDgpgBA0hIGcCyBDYBjAFgSwHYHMAeAFQBooA1APigF4oBvKAbRijygGt4B7AMyjEAugC5BrIVAgAPYBFwATBJSgB % 20 wfdg4ianwgankaf9m3ep0fca3acgo9qdbxz9ffomgbjamo8athaa8gbgafyq6mcmdg582poiwhagyvaj % 20 nj4tg5qcbe8ismp6znzdsa4 % 20 gpfdiuezfz8pacu % 20 hu1ume8paa2eci4tky2nn1rcp5byrhayj6tiegrdne5cqlj8klqaotrirqopc0qo6qxafmfw-y7eegfcofhp % 20 c5ffhvn7cfvc8nzwuucarxa32a % 20 honimtlgecizjeywqacwmui8wcixm5dq % 20 bo9duvy % 20 ib2slguwj-2g9amwomkvm8gsiwppaqbhqynqgbwbaxvnps2auihmqjuxbbuwytjrphwja0upKzpU2FzHZmE5zKsQA" rel="nofollow noreferrer">in this playground

导致

你只能在索引时使用类型,这意味着你不能使用const来进行变量引用:

例子

type Person = { age: number; name: string; alive: boolean };


const key = "age";
type Age = Person[key];

结果

Type 'any' cannot be used as an index type.

解决方案

使用类型来引用道具

例子

type key = "age";
type Age = Person[key];

结果

type Age = number