元素隐式具有“任何”类型,因为类型“字符串”的表达式不能用于索引类型-Phaser

时间:2020-06-14 20:05:22

标签: javascript typescript phaser-framework

我知道有人问过这个问题,但仍然无法为我的案件找到解决方案。

我有一个文件,在其中定义了某些值,然后循环遍历它们。我在这部分代码中得到了错误

preloadImages(){
    this.load.setPath("./assets/images");
    for (const key in STATIC.IMAGES) {
        if (STATIC.IMAGES.hasOwnProperty(key)) {
            this.load.image(STATIC.IMAGES[key], STATIC.IMAGES[key]);                
        }
    }
}

投诉来自STATIC.IMAGES [key]调用。 STATIC来自带有

的文件
export const STATIC = {

SCENES: {
    LOAD: "LOAD",
    MENU: "MENU"
},

IMAGES: {
    MENU_BG :"bg.png",
    MENU_TITLE: "title.png",
    MENU_PLAY: "play.png",
    MENU_SETTINGS: "settings.png",
    MENU_CREDITS: "credits.png",
    MENU_SPEAKER: "speaker.png",
    MENU_SPEAKER_MUTE: "speaker_mute.png"
},

SPRITES: {
    LOOP: { 
        name: "loop.png",
        size: 64
    }
},

AUDIOS: {
    BG_MUSIC: "airtone.mp3",
    POP: "pop.mp3",
    WOOSH: "woosh.mp3"
}};

不确定我理解为什么会发生这种情况以及如何解决它。

1 个答案:

答案 0 :(得分:1)

我们将其缩减为minimal, reproducible example。随时用您的obj替换下面的STATIC.IMAGES;两种方法的解释都相同:

let obj = { a: "hey", b: "you", c: "guys" };

for (const k in obj) {
  console.log(obj[k].toUpperCase()); // error!
  /* Element implicitly has an 'any' type because expression of type 
  'string' can't be used to index type '{ a: string; b: string; c: string; }'. */
}

在这里,我们有一个带有三个键的对象obj。我们尝试使用for..in循环遍历那些键,并且会收到一个错误,指出您无法使用k来索引到obj中。为什么?这不是for..in的要点吗?


这里的问题是TypeScript中的对象类型是 open extendible ,而不是 closed exact 。您可以在TypeScript中向对象添加额外的属性而不会违反其类型。在某些情况下这很好:它允许您通过添加属性来扩展接口和子类类。但是在其他情况下,这很痛苦:

编译器将obj视为类型{a: string, b: string, c: string}的值。由于此类型不正确,因此当您执行for (const k in obj)时,编译器知道k将采用值"a""b""c",但它会不知道这些是唯一可能的值:

let hmm = { a: "hey", b: "you", c: "guys", d: 12345 };
obj = hmm; // no error!

obj = hmm中没有错误,因为值hmm{a: string, b: string, c: string}匹配。 d属性不违反类型。因此,就所有编译器所知,for (const k in obj)会枚举"a""b""c"谁知道其他string值是什么。因此,k的类型不是keyof typeof obj。它只是string

使用类型为{a: string, b: string, c: string}的键索引到string是不安全的。确实,在将hmm分配给obj的情况下,将发生调用(12345).toUpperCase()的运行时错误。因此,对于编译器警告您来说,即使不是很有用,也是正确的。

有关此问题的答案的“官方”版本,请参见microsoft/TypeScript#35847。另请参阅Why doesn't Object.keys() return a keyof type in TypeScript?,该问题是相同的问题,除了使用Object.keys(obj).forEach(k => ...)而不是for (const k in obj) {...} ...而且答案相同。

如果曾经引入确切的类型,那么也许这个问题就会消失。为此有一个功能请求:microsoft/TypeScript#12936。但这看起来不会很快发生。


那么,现在可以做什么?好吧,如果我们确定知道obj不会有编译器不知道的任何其他键,我们可以将其视为确切的类型,而只是告诉编译器不要担心它。这种“告诉编译器”采用type assertion的形式:

for (const k in obj) {
  console.log(obj[k as keyof typeof obj].toUpperCase()); // okay
}

在这里,k as keyof typeof obj是类型断言。我们必须注意不要以这种方式对编译器撒谎,但是只要我们 保持谨慎,就可以了。


为了完整起见,您还可以在循环外声明k并为其指定更窄的类型。就类型安全而言,这等效于类型断言:

let k: keyof typeof obj;
for (k in obj) {
  console.log(obj[k as keyof typeof obj].toUpperCase()); // okay
}

好的,希望能有所帮助;祝你好运!

Playground link to code