在编译带有noImplicitAny标志的typescript时,如何防止错误"对象类型的索引签名隐式具有'任何'类型"?

Jas*_*lte 285 typescript

我总是使用标志--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];
Run Code Online (Sandbox Code Playgroud)

需要注意的重要一点是,关键变量来自应用程序中的其他位置,可以是对象中的任何键.

我试过通过以下方式明确地转换类型:

let secondValue: string = <string>someObject[key];
Run Code Online (Sandbox Code Playgroud)

或者我的情景是不可能的--noImplicitAny

tho*_*epo 295

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

在你的情况下,将是 [key: string]: string;

interface ISomeObject {
    firstKey:      string;
    secondKey:     string;
    thirdKey:      string;
    [key: string]: string;
}
Run Code Online (Sandbox Code Playgroud)

但是,这也强制所有属性类型与索引签名匹配.由于所有属性都是string有效的.

虽然索引签名是描述数组和"字典"模式的强大方法,但它们还强制所有属性都与其返回类型匹配.

编辑:

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

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

// Type of `secondValue` is `string|IOtherObject`
let secondValue = someObject[key];
// Type of `foo` is `string`
let foo = secondValue + '';
Run Code Online (Sandbox Code Playgroud)

虽然如果索引签名中有很多不同的类型,这可能会有点混乱.替代方案是any在签名中使用.[key: string]: any;然后你需要像上面那样投射类型.


Ped*_*rde 171

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

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

唯一的问题是,这不再是类型安全的,正如你所追求的那样any.但是你总是可以回到正确的类型.

ps:我正在使用typescript 1.7,不确定以前的版本.

  • 为了避免tslint警告,你也可以使用:`let secondValue:string =(someObject as any)[key];` (16认同)

Pio*_*ski 85

TypeScript 2.1引入了处理此问题的优雅方式.

const key: (keyof ISomeObject) = 'secondKey';
const secondValue: string = someObject[key];
Run Code Online (Sandbox Code Playgroud)

我们可以在编译阶段按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];
Run Code Online (Sandbox Code Playgroud)

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

进一步阅读更多keyof用法.

  • 但是,如果我们将`key`声明为`const key =(keyof ISomeObject)`='second'+'Key',它将无法工作 (6认同)

Sco*_*nro 51

以下tsconfig设置将允许您忽略这些错误 - 将其设置为true.

suppressImplicitAnyIndexErrors

为索引缺少索引签名的对象抑制noImplicitAny错误.

  • 这是你不应该做的事情 - 你团队中的某个人可能已经明确设置了这个编译器选项,使代码更具防弹性! (14认同)
  • 我不同意这正是这个选项的用途:允许括号表示`--noImplicitAny`.匹配完美op的问题. (12认同)
  • 我同意@Ghetolay.如果无法修改接口,这也是唯一的选择.例如,使用像`XMLHttpRequest`这样的内部接口. (4认同)

小智 20

上面提到的'keyof'解决方案有效.但是,如果变量仅使用一次,例如循环遍历对象等,您也可以对其进行类型转换.

for (const key in someObject) {
    sampleObject[key] = someObject[key as keyof ISomeObject];
}
Run Code Online (Sandbox Code Playgroud)


als*_*ang 14

使用 keyof typeof

const cat = {
    name: 'tuntun'
}

const key: string = 'name' 

cat[key as keyof typeof cat]
Run Code Online (Sandbox Code Playgroud)

  • @Dweep 当您想说您所指的键属于某个接口时,请使用“as keyof”。当您没有某个对象的接口并希望编译器猜测您所引用的对象的类型时,请使用“as keyof typeof”。 (2认同)

Sim*_*ver 8

创建一个接口来定义“索引器”接口

然后使用该索引创建您的对象。

注意:这仍然会有其他答案在强制执行每个项目的类型方面描述的相同问题 - 但这通常正是您想要的。

您可以根据需要制作泛型类型参数: 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];
Run Code Online (Sandbox Code Playgroud)

打字稿游乐场


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

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

然后T可以在类内部建立索引:-)


Ste*_*ush 7

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

const config: MyConfig = { ... };

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


Sup*_*hne 6

像这样声明对象.

export interface Thread {
    id:number;
    messageIds: number[];
    participants: {
        [key:number]: number
    };
}
Run Code Online (Sandbox Code Playgroud)


Sim*_*ver 6

没有索引器?然后自己做!

我已将其全局定义为定义对象签名的简便方法。T可以根据any需要:

type Indexer<T> = { [ key: string ]: T };
Run Code Online (Sandbox Code Playgroud)

我只是添加indexer为班级成员。

indexer = this as unknown as Indexer<Fruit>;
Run Code Online (Sandbox Code Playgroud)

所以我最终得到了这个:

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
Run Code Online (Sandbox Code Playgroud)

这样做的好处是您可以找回正确的类型-使用的许多解决方案<any>都会为您丢失键入内容。请记住,这不会执行任何运行时验证。如果您不确定某物是否存在,则仍然需要检查该物是否存在。

如果您要过于谨慎,并且正在使用strict,则可以执行此操作以显示可能需要进行显式未定义检查的所有位置:

type OptionalIndexed<T> = { [ key: string ]: T | undefined };
Run Code Online (Sandbox Code Playgroud)

我通常不认为这是必要的,因为如果我从某个地方获得字符串属性,那么我通常知道它是有效的。

如果我有很多需要访问索引器的代码,并且输入可以在一个地方更改,我发现这种方法特别有用。

注意:我正在使用strict模式,这unknown绝对是必要的。

编译后的代码将是indexer = this,因此它与_this = this为您创建typescript时非常相似。


O.A*_*set 5

声明键是字符串的类型,值可以是任何类型,然后用这种类型声明对象,lint 不会出现

type MyType = {[key: string]: any};
Run Code Online (Sandbox Code Playgroud)

所以你的代码将是

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

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

    let key: string = 'secondKey';

    let secondValue: string = someObject[key];
Run Code Online (Sandbox Code Playgroud)