使用多个可选类型时打字稿错误

Bra*_*ven 5 javascript typescript

这是错误的示例:

interface Block {
  id: string;
}

interface TitleBlock extends Block  {
  data: {
    text: "hi",
    icon: "hi-icon"
  }
}

interface SubtitleBlock extends Block  {
  data: {
    text: "goodbye",
  }
}

interface CommentBlock extends Block {
  author: string; 
}

type BlockTypes = TitleBlock | SubtitleBlock | CommentBlock

function mapper(block : BlockTypes) {
    if(block?.data?.text){  // TS Error here!!! 
      /** do something */
    }
}
Run Code Online (Sandbox Code Playgroud)

实例

当块具有特定属性时,我想在函数中执行某些操作,但 TS 不允许我这样做,即使 BlockType 中存在具有该属性的类型。

管理此类功能的最佳方法是什么?

jca*_*alz 4

可选的链接运算符 (?. )防止nullundefined;它不会将联合类型表达式过滤为已知包含该键的成员。请参阅Microsoft/TypeScript#33736 中的此评论以获得规范答案。

\n
\n

问题在于 TypeScript 中的对象类型是开放的,并且允许包含比编译器所知更多的属性。因此,如果block.data为真,不幸的是并不意味着block.data.text存在:

\n
const oof = {\n  id: "x",\n  author: "weirdo",\n  data: 123\n}\nmapper(oof); // accepted\n
Run Code Online (Sandbox Code Playgroud)\n

oof是一个有效的CommentBlock,所以mapper(oof)是允许的,并且oof.data是真实的,但是oof.data.textundefined,如果你尝试将它视为 ,你将会遇到问题string

\n

因此,允许可选链接按照您想要的方式过滤联合是不健全的(即类型不安全)。

\n
\n

如果您认为不太oof可能出现类似情况,这里的解决方法是使用运算in符作为类型保护

\n
function mapper(block: BlockTypes) {\n  if ("data" in block) {\n    block.data.text.toUpperCase()\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这工作正常,没有编译器错误。当然,它仍然不健全,与缩小范围完全相同?.......如果您调用,则mapper(oof)不会出现编译器错误,但您会收到运行时错误。所以你应该小心。

\n

inTypeScript 允许以这种方式运行,但不允许其他类似的构造,这有点奇怪。您可以阅读microsoft/TypeScript#10485中的讨论来了解为什么会发生这种情况。大多数情况下,人们在写作时"foo" in bar很少会做错事,但其他结构(如)bar.foo && bar.foo.baz被误用的情况更频繁。但这是 TS 团队的观点,而不是我的观点。\xe2\x80\x8d\xe2\x99\x82\xef\xb8\x8f

\n

Playground 代码链接

\n