我总是使用标志--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
无法实现我的方案?
答案 0 :(得分:258)
添加索引签名将让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;
然后你需要像上面那样投射类型。
答案 1 :(得分:164)
避免错误的另一种方法是使用这样的强制转换:
let secondValue: string = (<any>someObject)[key];
(注意括号)
唯一的问题是这不再是类型安全的,因为你正在向any
施放。但是你总是可以回到正确的类型。
ps:我使用的是typescript 1.7,不确定以前的版本。
答案 2 :(得分:65)
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
选项)
使用more keyof
usages进一步阅读。
答案 3 :(得分:51)
以下tsconfig setting将允许您忽略这些错误 - 将其设置为true。
<强> suppressImplicitAnyIndexErrors 强>
禁止noImplicitAny错误索引缺少索引签名的对象。
答案 4 :(得分:17)
上面提到的'keyof'解决方案有效。但是,如果变量仅使用一次,例如循环遍历对象等,则还可以对其进行类型转换。
for (const key in someObject) {
sampleObject[key] = someObject[key as keyof ISomeObject];
}
答案 5 :(得分:8)
使用keyof typeof
const cat = {
name: 'tuntun'
}
const key: string = 'name'
cat[key as keyof typeof cat]
答案 6 :(得分:7)
与@Piotr Lewandowski的答案类似,但在forEach
内:
const config: MyConfig = { ... };
Object.keys(config)
.forEach((key: keyof MyConfig) => {
if (config[key]) {
// ...
}
});
答案 7 :(得分:6)
声明这样的对象。
export interface Thread {
id:number;
messageIds: number[];
participants: {
[key:number]: number
};
}
答案 8 :(得分:5)
我已将其全局定义为定义对象签名的简便方法。如果需要,//= require rails-ujs
//= require jquery
//= require materialize
//= require turbolinks
//= require_tree .
$(document).on('turbolinks:load', function() {
$('.dropdown-trigger').dropdown();
});
可以是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
时非常相似。
答案 9 :(得分:3)
然后使用该索引创建对象。
注意:仍然存在其他问题,这些问题在执行每个项目的类型方面都存在相同的问题-但这通常正是您想要的。
您可以根据需要设置通用类型参数: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];
定义通用类型时,甚至可以在通用 constraint 中使用它:
export class SmartFormGroup<T extends IndexableObject<any>> extends FormGroup
然后可以在类内的T
进行索引:-)
答案 10 :(得分:1)
在3个步骤中使用 Typescript 3.1 可以找到的最简单的解决方案是:
1)制作界面
interface IOriginal {
original: { [key: string]: any }
}
2)键入副本
let copy: IOriginal = (original as any)[key];
3)在任何地方使用(包括JSX)
<input customProp={copy} />
答案 11 :(得分:1)
声明类型,其键为字符串,值可以为任意 然后用这种类型声明对象,棉绒就不会出现
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 :(得分:0)
今天,更好的解决方案是声明类型。像
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 :(得分:0)
我有两个界面。首先是其他的孩子。我做了以下事情:
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;
}
答案 14 :(得分:0)
无需使用ObjectIndexer<T>
或更改原始对象的界面(如大多数其他答案中所建议的)。
您可以使用以下命令将关键字的选项范围缩小到字符串类型:
type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];
这个很棒的解决方案来自an answer to a related question here。
就像您缩小到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];
您找到此示例 in this playground 。