Typescript 数组:使用混合类型推理

2023-12-12

打字稿版本 3.0.3。

我正在为侧边栏导航菜单创建一个模型,项目可以是如下所述的两种类型之一:

type SidebarItems = Array<SimpleSidebarItem | ComplexSidebarItem>;

abstract class SidebarItem {
    title: string;
}

class SimpleSidebarItem extends SidebarItem {
    url : string;
}

class ComplexSidebarItem extends SidebarItem {
    subItems: Array<{
        title: string;
        url : string;
    }>
}
  • 如果它是 SimpleSidebarItem,则它必须有 url,但没有 subItems。
  • 它是一个 ComplexSidebarItem,它不应该有 url,但必须有 subItems。

我无法让这个正常工作 - 这不应该是有效的输入,但它显示正常:

const items: SidebarItems = [{title: '', url: '', subItems: [{title: '', url: ''}]}];

推理未按预期进行:

const items: SidebarItems = [{title: '', url: ''}, {title: '', subItems: [{title: '', url: ''}]}];
const shouldBeComplexSidebarItem = items[1];

shouldBeComplexSidebarItem 的类型是 SimpleSidebarItem |复杂侧边栏项目。

我在这里缺少什么?


这里有两个问题。

第一个与工会参与时的过多财产检查有关。你可以阅读这个答案here类似的问题。其要点是,对联合的多余属性检查允许任何成员的任何键出现在对象上。我们可以通过引入 never 类型的额外成员来解决这个问题,以确保具有多余属性的对象不会错误地与特定成员兼容:

type SidebarItems = Array<StrictUnion<SimpleSidebarItem | ComplexSidebarItem>>;

type UnionKeys<T> = T extends any ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>


const items2: SidebarItems = [{title: '', url: '', subItems: [{title: '', url: ''}]}]; //error
const items3: SidebarItems = [{title: '', subItems: [{title: '', url: ''}]}]; //ok

第二个问题与以下事实有关:如果您指定变量的类型,TypeScript 不会进行任何额外的推断,因此items[1] is ComplexSidebarItem丢失了,所有打字稿都会知道一个项目可以SimpleSidebarItem | ComplexSidebarItem .

我们可以使用类型保护来检查类型:

const items: SidebarItems = [{title: '', url: ''}, {title: '', subItems: [{title: '', url: ''}]}];
const shouldBeComplexSidebarItem = items[1];
if(!('url' in shouldBeComplexSidebarItem)){ //type guard
    shouldBeComplexSidebarItem.subItems // is ComplexSidebarItem here 
} 

或者我们可以使用一个函数来创建将推断元组类型的数组,其中特定索引处的类型是已知的:

function createItems<T extends SidebarItems>(...a:T){
    return a;
}
const items = createItems({title: '', url: ''}, {title: '', subItems: [{title: '', url: ''}]});
const shouldBeComplexSidebarItem = items[1];
shouldBeComplexSidebarItem.subItems // is an object literal compatible with ComplexSidebarItem

您还可以手动指定元组类型,在这种情况下StrictUnion不再需要:

const items: [SimpleSidebarItem, ComplexSidebarItem] = [{title: '', url: ''}, {title: '', subItems: [{title: '', url: ''}]}];
const shouldBeComplexSidebarItem = items[1];
shouldBeComplexSidebarItem.subItems // is ComplexSidebarItem
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Typescript 数组:使用混合类型推理 的相关文章

随机推荐

  • C 函数中的全局变量和返回多个可变长度数组(指针)

    我有一些编程经验 但 C 语言不多 我有一个相当大的 C 文件 其中有多个按顺序执行的函数 因此在这种特殊情况下 实际上没有函数被调用两次 为了便于阅读 它们被分解 因为每个函数仍然有单独的目的 该程序对几个可变长度的 double 长数组
  • 如何使用 Windows 7 中的自动运行从闪存驱动器打开网页?

    我一直在尝试想出一种方法来自动运行一系列托管在 USB 闪存驱动器上的 HTML 页面 这些都是静态页面 并且都链接到我的闪存驱动器根目录中的index html 我尝试设置一个 autorun inf 文件 如下所示 autorun op
  • 将 require 与相对路径结合使用

    我们对 Protractor 有一组相当大的端到端测试 我们遵循页面对象模式 这有助于我们保持测试的干净和模块化 我们还有一组辅助函数可以帮助我们遵循干原则 问题 单个规范可能需要多个页面对象和辅助模块 例如 use strict var
  • 如何从 Process.GetCurrentProcess().Threads 获取托管线程

    我可以从 Process GetCurrentProcess Threads 获取正在运行的线程列表 但我需要知道以 Thread Start 启动的线程的托管名称 但它不是 ProcessThread 对象的属性 有没有办法从 Proce
  • 为什么这段代码的JTextArea占据了整个JFrame?

    我希望框架的一部分包含 JTextArea 但它完全占据了 我无法在这里追踪错误 import java awt import javax swing public class EchoServer public static void m
  • 在空手道中从 JSON 响应中的数组获取最大值

    我有以下 Json 作为 API 调用的响应 location name London region City of London Greater London country United Kingdom lat 51 52 lon 0
  • contentEditable 元素中的拖放事件

    当将某些内容放入 contentEditable 元素中 拖动后 时会触发什么事件 我说的是普通的旧式拖放 而不是 HTML5 拖放 其中任何元素都可以拖动 用例很简单 页面上有一个 contentEditable div 用作编辑器 用户
  • Tensorflow 2.0 - AttributeError:模块“tensorflow”没有属性“Session”

    当我执行命令时sess tf Session 在 Tensorflow 2 0 环境中 我收到如下错误消息 Traceback most recent call last File
  • 使用 Turbo C++ 3.0 处理键盘中断

    我有一个项目 那是一个简单的游戏 落块 游戏区域被视为一个网格 其大小为 20x20 屏幕顶部会有掉落的方块 底部有一个英雄 他会射击方块 游戏的目标是在方块到达底线之前将其射出 他始终坚守在底线 每当用户按下键盘的空格键时 我都会生成一个
  • 将鼠标悬停在另一个元素上更改元素样式

    我有三个元素 我需要通过将鼠标悬停在其他两个元素上来更改一个元素的样式 html div class pagination span class step links if page obj has previous div class n
  • webkit 中忽略 document.open("text/plain") 格式(safari、chrome)

    我正在使用 document open text plain 和 document write 从 JavaScript 创建页面 要呈现的文本是多行制表符分隔的文本 在Chrome 13 0 782 220 m和Safari 5 0 5中
  • 查找字符串中反斜杠的出现次数

    如果我的字符串是 aud ios 我如何检查它是否存在 in it 我尝试使用preg match string 但它不起作用 实现这一目标的正确方法是什么 对于这样简单的事情 您不需要正则表达式 像这样的字符串函数strpos 应该足够了
  • 如何捕获完整的 tableView ios 屏幕截图

    我有一个有 30 行的 tableView 并且在 tableView 的顶部还有一个视图 不在 tableview 标题中 我想捕获屏幕的完整屏幕截图 包括视图和 tableview 的所有行 但我只能能够捕获表视图和视图的可见行 请帮助
  • 获取父目录的相对路径

    我有一个场景 我想获取返回特定父目录的路径 这是一个示例文件夹结构 something 是一个文件夹 index php components header php footer php pages somePage php somePag
  • 尝试在空对象引用上调用接口方法“int android.database.Cursor.getCount()”[重复]

    这个问题在这里已经有答案了 我确实有一个让我头疼的问题 我通过自定义内容提供程序将我的城市的一些图像存储在 sqlite 数据库中 但是 当我运行我的应用程序时 我得到一个空光标 Caused by java lang NullPointe
  • 从 Scala (shark-shell) 访问 Shark 表 (Hive)

    I have shark 0 8 0其运行于hive 0 9 0 我可以通过调用在 Hive 上进行编程shark 我创建了一些表并向它们加载了数据 现在 我尝试使用这些表访问数据Scala 我调用了Scala外壳使用shark shell
  • Android - adb tcpip 错误

    我想使用以下方式连接到 Android 设备adb通过网络 我熟悉如何使用 ADB over TCP 连接到 Android 与https developer android com guide topics usb index html并
  • MonoTouch MVVM与实例变量的交叉绑定

    这听起来可能真的很愚蠢 但我就是无法绑定到实例变量 字符串类型 来工作 在我视图的 xib 中 我创建一个文本字段作为 IB 中的出口 然后我可以将其绑定到我的 viewModel 的字符串属性 但是 它不允许我以相同的方式将视图的字符串变
  • C#中相对路径到绝对路径?

    我有 xml 文件 其中包含图像的 href 文件路径 例如 images image jpg href 包含相对路径 现在 我需要提取图像的 href 并将它们转换为文件系统中的绝对路径 我知道 GetFullPath 方法 但我尝试了它
  • Typescript 数组:使用混合类型推理

    打字稿版本 3 0 3 我正在为侧边栏导航菜单创建一个模型 项目可以是如下所述的两种类型之一 type SidebarItems Array