创建并返回可分配给对象的特定键的值

2023-12-24

我有以下代码,应该(在这个简化的应用程序中)返回一个可分配给特定键的值Obj.

interface Obj {
  foo: number;
  bar: string;
}

const foo = createValueOfObjKey("foo");
const bar = createValueOfObjKey("bar");

function createValueOfObjKey<Key extends keyof Obj>(key: Key): Obj[Key] {
  switch (key) {
    case "foo":
      return 0;
    case "bar":
      return "";
  } 
}

然而,在两个 return 语句上,编译器都会抱怨(例如"foo"return) 关于无法分配给 never:

Type 'number' is not assignable to type 'Obj[Key]'.
  Type 'number' is not assignable to type 'never'.(2322)

(有趣的是,保存返回值的常量类型被正确推断,例如const foostring, and const bar number)

打字稿似乎无法缩小范围Obj[Key] to Obj["foo"]。所以我想也许引入一个局部变量会有所帮助:

    case "foo":
      // I have also tried other things, like Obj[Key].
      const val: Obj[typeof key] = 0;
      return val;

但遗憾的是,这仍然导致:

Type 'number' is not assignable to type 'Obj[Key]'.
  Type 'number' is not assignable to type 'never'.(2322)

我发现了以下问题,但我不确定它们是否相关:

  • https://github.com/microsoft/TypeScript/issues/20907 https://github.com/microsoft/TypeScript/issues/20907
  • https://github.com/microsoft/TypeScript/issues/10530 https://github.com/microsoft/TypeScript/issues/10530

这里提到的代码作为游乐场。 https://www.typescriptlang.org/play?ts=4.9.0-dev.20220921#code/JYOwLgpgTgZghgYwgAgPICMBWyDeAoZZGAe2IC5kQBXAW3WgG4Dl04oKBnMKUAcyYC+ePAmIguRUsgC8yBFAhxIANTgAbKhFQwMmANIQAngAoARCWKmAlE1HiwLNjLkKlEVRq06sBk6dZQ1kx4MFQgCGDAYi6KKuqa2rq+ADy+yBAAHpAgACYcyADWRsQwaFgAfMZFhhS+VhS6ANq+ALq4zBwA7sBgCAAWyFVGVu2EhAhwHCjmpKZkzGPIAPRLyACSyH1wAG4o6hzEyNzAEDnIxGB90Ed9fBwANMhqwEVlmM1GLQB0C2N2Ett1A0sI0wIYAA4QEqFT7OAAMTEWhAUYCoUBAyEBakRf0m0wCc1+yIgqPRyFMphxAmQeAEQA


从 TS4.8 开始,TypeScript 无法使用控制流分析 https://www.typescriptlang.org/docs/handbook/2/narrowing.html#control-flow-analysis缩小或重新限制generic https://www.typescriptlang.org/docs/handbook/2/generics.html类型参数。所以在createValueOfObjKey(key) where key是泛型类型K extends keyof Obj,你可以检查key via switch,这将缩小类型key... but K本身将顽固地保持不变。因此编译器不知道字符串或数字是否可以分配给Obj[K]它抱怨。

这是 TypeScript 目前缺少的功能。有一个悬而未决的问题微软/TypeScript#33014 https://github.com/microsoft/TypeScript/issues/33014要求添加对您编写的代码的支持。除非实施该措施或类似措施,否则只有解决方法。


最简单的解决方法就是assert https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions你所做的事情是正确的。它是类型安全性最低的,但不需要您对实现进行太多更改:

function createValueOfObj<K extends keyof Obj>(key: K): Obj[K] {
  switch (key) {
    case "foo":
      return 0 as Obj[K]; // assert
    case "bar":
      return "" as Obj[K]; // assert
  }
  throw new Error("UNREACHABLE"); // compiler can't see exhaustive so you need this 
}

如果您想要更多的类型安全性,那么您需要编写一些编译器可以验证准确的内容。为索引访问类型 https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html like T[K],如果您采用该类型的对象,编译器只能轻松验证您是否生成了该类型的有效值t和类型键k并返回值t[k]。也就是说,您可以执行索引访问手术获得可验证的索引访问type.

所以你可以写

function createValueOfObj<K extends keyof Obj>(key: K): Obj[K] {
  return {
    foo: 0,
    bar: ""
  }[key]; // okay
}

编译没有错误,万岁!

如果您担心这会迫使您提前提出所有可能的输出值,您可以通过延迟实现此查找对象getters https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get而不是常规属性:

function createValueOfObjKey<K extends keyof Obj>(key: K): Obj[K] {
  return {
    get foo() { return 0 },
    get bar() { return "" }
  }[key]; // okay
}

编译也没有错误,现在只有与实际对应的代码key将被执行。


让我们验证一下它是否有效:

const foo = createValueOfObjKey("foo");
console.log(foo.toFixed(2)) // "0.00"
const bar = createValueOfObjKey("bar");
console.log(bar.toUpperCase()) // ""

看起来不错。

Playground 代码链接 https://www.typescriptlang.org/play?ts=4.8.2#code/JYOwLgpgTgZghgYwgAgPICMBWyDeAoZZGAe2IC5kQBXAW3WgG4Dl04oKBnMKUAcyYC+eYXhhUQCMMGIhkCKBDiQAanAA2VCKhgZMAQQ4doYADwBpZBAAekEABMOyANYQAnsRhosAPgAUL1wozAEoKXQBtMwBdXGYOAHdgMAQAC2R-N2DYwkIEOCNkACISYkKyZhzkBTAqKFkABmR8r0xIqKZKvILC1igyipzq2tlCwqbHCOiO5CFCMBSoYnjKCGWAUShFqF9CgFUAOQAlNb0AYQAJPQAhABk1wuDBYTEJKRk5BSUIVQ0tHSwAJJ2KzmSw2CD2RwBDwtPwBIKhFptbJVCA1OoowglCj1AA0A16FFGzAE4QC7TwQhEL0k0lk8kUKnUmm0ujMblB1lsDmcbhhujhbgRYSwyPwhCGGPFOV4aKIpF8WRwqPRDRm+MqsrALDYitwKuGRTGsxmZLcFKpCBkXHlxGQAF4Pozvsy-my3DsSg8mFaQBxiGoIAA6NTEXi+EpBsDEABiwCsEDsvgATMEsgB6dNFepB+r1Qp4X023oOp1fH4s-6YdmuHa9b2F60B4Oh8O9KPEXYABy70FO+QgiozWeJQA

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

创建并返回可分配给对象的特定键的值 的相关文章

随机推荐

  • 如何在抓取网站时到达最后一页后停止 selenium webdriver?

    网站上的数据量 页面数 不断变化 我需要抓取循环分页的所有页面 网站 https monentreprise bj page annonces 我尝试过的代码 xpath id yw3 li 12 a while True next pag
  • 如何从 VB.NET 对话框中获取值?

    我有一个 frmOptions 表单 其中有一个名为 txtMyTextValue 的文本框和一个名为 btnSave 的按钮 用于在单击时保存并关闭表单 然后 当单击主窗体 frmMain 上的按钮 btnOptions 时 我将显示此对
  • 每次我到处使用 struct 而不是 class 时,我会杀死一只小猫吗?

    struct默认情况下是公开的 而class默认情况下是私有的 让我们以 Ogre3D 为例 如果我改变一切class发生与结构 它编译 我猜 并且引擎像以前一样工作 如果我是对的 编译后的代码与以前完全相同 因为只有编译器检查私有 受保护
  • 如何使用console.log(Javascript)打印函数的输出[重复]

    这个问题在这里已经有答案了 以下代码的意思是打印带有名称的名称字符串 然而 这是不正确的 var nameString function name return Hi I am name nameString Amir console lo
  • 如何在一块玻璃上绘制多个矩形?

    我正在尝试在玻璃板上绘制一系列矩形 如中所述here http docs oracle com javase tutorial uiswing components rootpane html glasspane 问题是 窗格中仅显示列表中
  • Python 打印参数结束

    如何在使用 end 时从输出中删除最后一个 add gt 我在这里没有使用 sep bcoz sep 在这里不会有任何效果 因为 print 语句在这里一次只打印 1 项并以 incr 结尾我的 def fibonaci num n if
  • Google Protocol Buffers,如何处理多种消息类型?

    是否可以获得序列化的Protocol Buffer消息的Type 我有这个例子 option java outer classname ProtoUser message User required int32 id 1 required
  • 在ES6中,如何检查对象的类?

    在ES6中 如果我创建一个类并创建该类的对象 如何检查该对象是否是该类 我不能只用typeof因为物体仍然 object 我只是比较构造函数吗 Example class Person constructor var person new
  • Visual Studio 2012 最新项目丢失

    在 Windows 任务栏和开始菜单中 最近打开的项目 解决方案列表为空 然而 在VS中 常规选项被标记为显示10个项目 该列表曾经显示 但最近我发现它消失了 有任何想法吗 我遇到了完全相同的问题 但我感觉其他菜单项也丢失了 我所做的是进入
  • 捕获组引用+数字

    例如 我想用第一个捕获组替换字符串1附加到它 我想要做 11 解释为 1 and 1 但这并不适用于所有口味 我该怎么办 信息位于视网膜链接 https github com mbuettner retina你提供的说 在引擎盖下 它使用
  • 四倍精度特征值、特征向量和矩阵对数

    我正在尝试以四倍精度对矩阵进行对角化 并取它们的对数 有没有一种语言可以使用内置函数来完成此任务 请注意 标签中的语言 包还不够 存在以下缺陷 Matlab 不支持四精度 Python NumPy SciPy 数据类型为 float128
  • 使用什么算法/方法来同步多个视频播放器

    动机 我目前正在尝试同步两个联网的 raspi 上的两个视频 我尝试从桌面 http 和 udp 进行实时流式传输 但每个 raspi 仍然以明显的延迟打开流 接下来我尝试在 raspi 上安装 vlc 并与桌面 vlc 同步 但这也不起作
  • 如何从 PHP 日期时间获取 unix 时间戳?

    我正在尝试使用 PHP 获取 unix 时间戳 但它似乎不起作用 这是我尝试转换为 unix 时间戳的格式 PHP datetime 2012 07 25 14 35 08 unix time date Ymdhis strtotime d
  • 如何在 Linux 上拦截来自 USB 设备的消息?

    我有一个流行的绘图板 我用 USB 连接到我的电脑 连接后 平板电脑会检测手部动作并相应地操纵指针 在某个地方 平板电脑正在将这些数据传输到我的计算机 我的目标是拦截这些传输并在处理数据后操纵鼠标 我发现的流行语是 设备驱动程序 and H
  • HTML 步进器中单位的显示

    我希望用户输入带有 cm kg 或 等单位的数字 这可以在 jQuery UI 中完成 Example http jqueryui com spinner currency 但是 我想用纯html实现它 例如 input display i
  • 读取列中包含逗号的 CSV 文件

    我有一个包含 6 列的 csv 文件 其中一列的文本以逗号分隔 例如 BOLT RD HD SQ SHORT NECK METRIC 当我在 R 中读取该文件时 该列发生溢出 随后数据移动到新行 下面我粘贴几行 014003051906 E
  • CheckboxSelectMultiple 的初始值

    我正在使用以下方法初始化表单 MultiSubscriptionForm initial email user email 在我的表单中 我还想初始化一个 CheckboxSelectMultiple 小部件来检查一组复选框 我怎样才能做到
  • Visual Studio 2015 更新 1 的 MicroUpdate 1.1

    在全新的 Windows 7 Ultimate 64 位计算机上通过 MSDN 下载的 ISO 映像安装 Visual Studio 2015 Enterprise 后 安装程序最终显示以下错误消息 MicroUpdate 1 1 for
  • Facebook iOS PresentRequestsDialogModallyWithSession 返回选定的朋友

    我正在开发一个 iOS 应用程序 我希望能够通过 facebook 向我的应用程序发送邀请 我设法使用 PresentRequestsDialogModallyWithSession 但我也希望我的应用程序知道邀请发送给了谁 这可能吗 是的
  • 创建并返回可分配给对象的特定键的值

    我有以下代码 应该 在这个简化的应用程序中 返回一个可分配给特定键的值Obj interface Obj foo number bar string const foo createValueOfObjKey foo const bar c