键入动态对象引用

时间:2019-01-31 14:51:59

标签: typescript

我有一个assets文件夹的索引文件,该文件已对图像导出进行了分组,如下图所示

// assets/index.ts
export const UI_ELEMENTS = {
  BUTTON_BLUE_ACTIVE: require("./button_blue_active.png"),
  BUTTON_BLUE_DISABLED: require("./button_blue_disabled.png"),
  BUTTON_BLUE_HOVER: require("./button_blue_hover.png")
}

然后我继续在某些文件中使用它

import { UI_ELEMENTS } from "./assets/index.ts"

const buttonImage = UI_ELEMENTS[`BUTTON_BLUE_${variant}`]

如您所见,变体是动态的,即根据情况可以是ACTIVE DISABLEDHOVER

我收到buttonImage的以下类型错误

  

[ts]元素隐式具有“ any”类型,因为类型为“ {   BUTTON_BLUE_ACTIVE:任意; BUTTON_BLUE_DISABLED:任意; BUTTON_BLUE_HOVER:任意; }'没有索引   签名。 [7017]

我试图弄清楚如何键入UI_ELEMENTS以便保持自动完成功能有效并摆脱此错误。

2 个答案:

答案 0 :(得分:2)

对于您而言,应该将每个对象明确标记为任意对象。避免尝试使用--noImplicitAny来摆脱消息的诱惑。

export const UI_ELEMENTS = {
  BUTTON_BLUE_ACTIVE: require("./button_blue_active.png") as any,
  BUTTON_BLUE_DISABLED: require("./button_blue_disabled.png") as any,
  BUTTON_BLUE_HOVER: require("./button_blue_hover.png") as any
}

或者您可以在后面加上一些类型安全性,以强制所有对象在文字上都与您一样具有相同的类型(除了既然它们是任意的,那部分就您而言没有多大帮助)。您仍然可以使用以下命令完成输入:

export type ObjectOf<T> = { [k: string]: T };

/**
 * Allows you to enforce that an object's properties are all of the same type and creates a type safe object that you
 * get auto complete for.
 *
 * Example:
 *
 * const Ab = createObjectOf<number>({a: true}); // Compile error
 *
 * const Ab = createObjectOf<number>({a: 1, b: 2});
 * Ab.a.toFixed() (with autocompletion)
 */
export function createObjectOf<TValue>() {
    return function <T extends ObjectOf<TValue>>(v: T) {
        return v;
    };
}

export const UI_ELEMENTS = createObjectOf<any>()({
  BUTTON_BLUE_ACTIVE: require("./button_blue_active.png"),
  BUTTON_BLUE_DISABLED: require("./button_blue_disabled.png"),
  BUTTON_BLUE_HOVER: require("./button_blue_hover.png")
})

答案 1 :(得分:1)

动态属性名称不允许TypeScript编译器推断所访问属性的类型。

不过,any很好,因为它是require("./xxx.png")的返回类型,所以anybuttonImage的正确类型。您只需要在保留自动完成功能的同时禁用错误消息即可。我建议三种方式:

1。将编译器配置为不太严格

更简单的方法就是禁用--noImplicitAny编译器选项。

2。或者,手动将UI_ELEMENTS的类型定义为index type及其属性

type UiElementsType = {
  [name: string]: any
  BUTTON_BLUE_ACTIVE: any,
  BUTTON_BLUE_DISABLED: any,
  BUTTON_BLUE_HOVER: any
}

const UI_ELEMENTS: UiElementsType = {
  BUTTON_BLUE_ACTIVE: require("./button_blue_active.png"),
  BUTTON_BLUE_DISABLED: require("./button_blue_disabled.png"),
  BUTTON_BLUE_HOVER: require("./button_blue_hover.png")
}

这是声明性的解决方案。此示例不是DRY,您必须将每个属性名称写入两次。但是也许您可以使用大写的属性名与小写的文件名相同的事实来动态填充UI_ELEMENTS对象?

3。或者,使用帮助程序来推断类型

function asIndexType<T>(obj: T): T & { [name: string]: any } {
  return obj
}

const UI_ELEMENTS = asIndexType({
  BUTTON_BLUE_ACTIVE: require("./button_blue_active.png"),
  BUTTON_BLUE_DISABLED: require("./button_blue_disabled.png"),
  BUTTON_BLUE_HOVER: require("./button_blue_hover.png")
})

在这里,我们声明一个辅助程序asIndexType,其类型与解决方案2中的类型相同。这是DRY,但辅助程序asIndexType将保留在编译后的代码中,而在运行时则不需要。