我总是用标志 --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
?
添加索引签名将使 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;
然后您需要像上面那样转换类型。
避免错误的另一种方法是像这样使用演员表:
let secondValue: string = (<any>someObject)[key];
(注意括号)
唯一的问题是这不再是类型安全的,因为您正在转换为 any
。但是你总是可以转换回正确的类型。
ps:我使用的是typescript 1.7,不确定以前的版本。
let secondValue: string = (someObject as any)[key];
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 进一步阅读。
key
声明为 const key = (keyof ISomeObject)
= 'second' + 'Key' 它将不起作用
以下 tsconfig setting 将允许您忽略这些错误 - 将其设置为 true。
suppressImplicitAnyIndexErrors 为索引缺少索引签名的对象抑制 noImplicitAny 错误。
--noImplicitAny
的括号表示法。完美匹配 op 的问题。
XMLHttpRequest
等内部接口。
keyof
运算符可以帮助保持一切严格,请参阅 Piotr 的回答!
使用keyof typeof
const cat = {
name: 'tuntun'
}
const key: string = 'name'
cat[key as keyof typeof cat]
keyof typeof
的用法
as keyof
当您想说您所指的密钥属于接口时。当您没有某个对象的接口并希望编译器猜测您所指的对象的类型时,请使用 as keyof typeof
。
key: any
一样错误。相反,您应该使 key
的类型实际上 BE keyof typeof cat: const key: keyof typeof cat = 'name'
这样您就不会盲目地转换字符串以使编译器安静。或者停止调用 key 一个字符串,使其类型推断:const key = 'name'
。或者使用 as const
来保证类型不是字符串,以防您希望 key 是变量:let key = 'name' as const
。有很多更好的选择,而不仅仅是对编译器撒谎,所以它保持安静。
key
可能来自其他地方,例如用户输入或解析的 json。
key
并假装它是 cat
中的键。在示例中,输入不是用户输入或 JSON,这是一个非常常见的用例,我们应该向人们展示如何正确和专业地做它,而不是围绕类型安全进行黑客攻击。
上面提到的“keyof”解决方案有效。但是如果变量只使用一次,例如循环一个对象等,你也可以对它进行类型转换。
for (const key in someObject) {
sampleObject[key] = someObject[key as keyof ISomeObject];
}
像这样声明对象。
export interface Thread {
id:number;
messageIds: number[];
participants: {
[key:number]: number
};
}
创建一个接口来定义'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];
在定义泛型类型时,您甚至可以在泛型约束中使用它:
export class SmartFormGroup<T extends IndexableObject<any>> extends FormGroup
然后类内的 T
可以被索引:-)
{ [key: string]: T }
的 Dictionary
的标准“内置”界面,但如果有,请编辑此问题以删除我的 ObjectIndexer
。
没有索引器?那就自己做吧!
我已将其全局定义为定义对象签名的简单方法。如果需要,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
时非常相似。
Record<T>
类型 - 我现在无法研究这方面的详细信息,但对于一些有限的情况,它可能会更好。
类似于@Piotr Lewandowski 的回答,但在 forEach
内:
const config: MyConfig = { ... };
Object.keys(config)
.forEach((key: keyof MyConfig) => {
if (config[key]) {
// ...
}
});
Argument of type '(field: "id" | "url" | "name") => void' is not assignable to parameter of type '(value: string, index: number, array: string[]) => void'
。我的代码看起来像 Object.keys(components).forEach((comp: Component) => {...}
,其中 Component
是一个类型(如 MyConfig
)。
声明类型,其键是字符串,值可以是任何类型,然后用这种类型声明对象,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];
我可以通过 3 个步骤使用 Typescript 3.1 找到的最简单的解决方案是:
1)制作界面
interface IOriginal {
original: { [key: string]: any }
}
2) 复印一份
let copy: IOriginal = (original as any)[key];
3)在任何地方使用(包括 JSX)
<input customProp={copy} />
今天更好的解决方案是声明类型。喜欢
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];
我有两个接口。首先是别人的孩子。我做了以下工作:
在父界面中添加了索引签名。使用 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];
这个出色的解决方案来自 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。
原因
您只能在索引时使用类型,这意味着您不能使用 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.
解决方案
使用类型来引用 props
例子
type key = "age";
type Age = Person[key];
结果
type Age = number
不定期副业成功案例分享