【问题标题】:Why does annotating this object with a Record type remove Intellisense?为什么使用 Record 类型注释此对象会删除 Intellisense?
【发布时间】:2021-09-15 15:32:47
【问题描述】:

我正在创建一个如下所示的数据文件:

interface ISprite {
  textureName: string,
  frame: Frame,
  origin: Vec2,
  zIndex?: number
}

export let sprites: Record<string, ISprite> = {
  monster: {
    textureName: "monster",
    frame: new Frame(0, 0, 32, 41),
    origin: new Vec2(16, 28),
    zIndex: -1
  },
  player: {
    textureName: "player",
    frame: new Frame(0, 0, 32, 32),
    origin: new Vec2(15, 32)
  }
};

如果我尝试从另一个文件中导入此数据文件,如下所示:

import { sprites } from "../data/sprites";

然后尝试访问这样的属性:

let player = sprites.player;

然后当我输入 sprites. 时,我没有得到 Intellisense(代码完成)

然而,我注意到,如果我从 sprites 变量声明中删除 Record&lt;string, ISprite&gt; 注释,我确实会获得智能感知。

但是,我相信我需要这个注释,因为我的一个函数只接受 ISprite 类型,而我不想让它接受 any

是否可以在保持代码完成的同时保持强类型?

【问题讨论】:

  • 试试sprites: {[k: string]: ISprite}
  • @EmanuelVintilă 同样的问题 =(
  • 因为在Record&lt;string, ISprite&gt; 中,string 可以是任意键。如果你想要智能感知,你需要用'monster' | 'player'之类的联合注释键。

标签: typescript record code-completion strong-typing


【解决方案1】:

这样做是因为Record&lt;string, ISprite&gt; 可以将 any 字符串作为键。 UI 无法提示每个字符串。

您可以为 UI 提供更多信息,使其显示 monsterplayer 以通过使用具有这些命名属性的类型与 Record&lt;string, ISprite&gt; 之间的交集来自动完成(但请继续阅读,您可能不需要):

export let sprites: {monster: ISprite, player: ISprite} & Record<string, ISprite> = {
// −−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  monster: {
    textureName: "monster",
    frame: new Frame(0, 0, 32, 41),
    origin: new Vec2(16, 28),
    zIndex: -1
  },
  player: {
    textureName: "player",
    frame: new Frame(0, 0, 32, 32),
    origin: new Vec2(15, 32)
  }
};

Playground link

但是

我相信我需要这个注释,因为我的一个函数只接受 ISprite 类型。

没关系。完全没有任何类型注释,您的对象在其monsterplayer 属性上的值与ISprite 兼容,因此您可以将它们传递给期望ISprite 的函数。 TypeScript 的类型系统是 structural(基于对象的形状),而不是 nominal(基于类型的名称),因此它很乐意接受任何对象作为 ISprite只要它具有兼容类型的所有必要属性。这很好用,例如:

export let sprites = {
    monster: {
      textureName: "monster",
      frame: new Frame(0, 0, 32, 41),
      origin: new Vec2(16, 28),
      zIndex: -1
    },
    player: {
        textureName: "player",
        frame: new Frame(0, 0, 32, 32),
        origin: new Vec2(15, 32)
    }
};

function example(sprite: ISprite) {
    console.log(sprite);
}

example(sprites.monster); // <=== Perfectly happy

Playground link

如果你想要类型名称,或者在sprites 中编写更多条目时提示ISprite 的成员,你可以给自己一个函数:

makeSprite(sprite: ISprite) {
    return sprite;
}

它看起来像无所事事,实际上是——在运行时。 (别担心,它并不贵)。但是在创作时,它可以让你这样做:

export let sprites = {
    monster: makeSprite({
      textureName: "monster",
      frame: new Frame(0, 0, 32, 41),
      origin: new Vec2(16, 28),
      zIndex: -1
    }),
    player: makeSprite({
        textureName: "player",
        frame: new Frame(0, 0, 32, 32),
        origin: new Vec2(15, 32)
    })
};

当您添加条目时,系统会在您为 makeSprite 编写 sprite 参数时提示您输入来自 ISprite 的属性名称。

【讨论】:

  • 不幸的是,这个对象很快就会有数百个属性,所以我认为我无法合理地保持这一点。我想我只需要删除类型注释并让函数采用any 代替.. 哦好吧..
  • 哦等等,实际上,我想我有一个潜在的解决方法的想法。我会尝试一下,看看它是否有效
  • @RyanPeschel - 嘿,当你发布你的 cmets 时,我正在更新答案。请参阅更新后的答案,没有理由让您的函数接受 any 而不是 ISprite
  • @RyanPeschel - 我刚刚根据您的评论进一步更新,您将添加更多这些内容。 :-D
  • 哦,完美!是的,我认为这比我打算做的更好!我正在考虑做这样的事情pastebin.com/JVRWLZj5,但我想我更喜欢你的更新答案
猜你喜欢
  • 2021-06-13
  • 2012-01-05
  • 2020-02-25
  • 2016-03-27
  • 1970-01-01
  • 2015-09-20
  • 2019-01-01
  • 2021-07-26
相关资源
最近更新 更多