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


当前回答

创建一个接口来定义'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];

打印稿操场


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

导出类SmartFormGroup<T extends IndexableObject<any>> extends FormGroup

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

其他回答

不需要使用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];

你可以在这个操场上找到这个例子。

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

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

创建一个接口来定义'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];

打印稿操场


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

导出类SmartFormGroup<T extends IndexableObject<any>> extends FormGroup

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

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

const config: MyConfig = { ... };

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

使用typeof键

const cat = {
    name: 'tuntun'
}

const key: string = 'name' 

cat[key as keyof typeof cat]