如何在类型包装器(映射类型)中转发(众所周知的)符号?

2023-11-30

按照文档映射类型,在 TypeScript 中应该可以有这样的包装器:

export type Wrapped<T> = {
  [P in keyof T]: T[P];
} & { _state: number }

function wrap<T extends object>(x: T): Wrapped<T> {
  let xWrapped = x as Wrapped<T>
  xWrapped._state = 0;
  return xWrapped;
}

总的来说,这似乎运作良好,Wrapped<T>行为完全像T.

然而,我注意到,在某些情况下,类型系统可能会不满意。例如:

let a = new Date()
let b = wrap(a)

function f(d: Date) {}

f(a) // works
f(b) // error: Property '[Symbol.toPrimitive]' is missing in type 'Wrapped<Date>' but required in type 'Date'

我该如何编写类型定义Wrapped<T>支持类似的类型Date?


背景:这个简化示例背后的实际问题出现在固体(框架),它使用这种包装器的递归版本其核心状态处理。在这种情况下,问题非常严重,因为 TypeScript 不接受Wrapped<MyState> for a MyState一旦处于嵌套状态的某个位置,就会出现“不可包装”类型,例如Date, 导致... as any as X无处不在。这个问题的目标是让 TypeScript 更容易使用 Solid。


我尝试过的:我已经对此进行了几周的修改,但没有成功。这2.9 发行说明提到映射类型{ [P in K]: XXX }现在也支持符号,但我不知道它在语法上看起来如何。

export type Wrapped<T> = {
  [P in keyof T]: T[P];
} & {
  [symbol in keyof T]?: T[symbol];  // doesn't work
} & { _state: number }

“有效”是将包装器写为

export type Wrapped<T> = {
  [P in keyof T]: T[P];
} & { _state: number } & {
  [Symbol.toPrimitive](hint: "string"): string;
  [Symbol.toPrimitive](hint: "default"): string;
  [Symbol.toPrimitive](hint: "number"): number;
}

但显然我不想无条件地将这些符号添加到所有包装类型中。基本上我正在寻找表达“如果符号在 T 中,则将其添加到包装类型”的语法。如果这通常不可能,是否至少可以针对一组众所周知的符号对其进行硬编码?

我在 GitHub 上发现了一些可能相关的问题,但作为 TypeScript 初学者,我无法理解它们:

  • #1863 允许使用符号进行索引
  • #24587 类型“symbol”不能用作索引类型。
  • #4538 改进对 Symbol.toPrimitive 的支持

您可以使用条件类型

type AddSymbolToPrimitiveDefault<T> = T extends 
  {[Symbol.toPrimitive](hint: "default"): string;} ? 
  {[Symbol.toPrimitive](hint: "default"): string;} : {};

type AddSymbolToPrimitiveString<T> = T extends 
  {[Symbol.toPrimitive](hint: "string"): string;} ? 
  {[Symbol.toPrimitive](hint: "string"): string;} : {};

type AddSymbolToPrimitiveNumber<T> = T extends 
  {[Symbol.toPrimitive](hint: "number"): number;} ? 
  {[Symbol.toPrimitive](hint: "number"): number;} : {};


type AddSymbolToPrimitiveTString<T> = T extends 
  {[Symbol.toPrimitive](hint: string): string | number;} ? 
  {[Symbol.toPrimitive](hint: string): string | number;} : {};

export type Wrapped<T> = {
  [P in keyof T]: T[P];
} & { _state: number } & AddSymbolToPrimitiveDefault<T>
 & AddSymbolToPrimitiveString<T> & AddSymbolToPrimitiveNumber<T> & AddSymbolToPrimitiveTString<T>

function wrap<T extends object>(x: T): Wrapped<T> {
  let xWrapped = x as Wrapped<T>
  xWrapped._state = 0;
  return xWrapped;
}

let a = new Date()
let b = wrap(a);

function f(d: Date) {}

f(a); // works
f(b); // works too

Update@jcalz 建议了一个简短的解决方案

type AddSymbolToPrimitive<T> = T extends 
  {[Symbol.toPrimitive]: infer V;} ? 
  {[Symbol.toPrimitive]: V;} : {};  

export type Wrapped<T> = {
  [P in keyof T]: T[P];
} & { _state: number } & AddSymbolToPrimitive<T>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在类型包装器(映射类型)中转发(众所周知的)符号? 的相关文章

随机推荐

  • 如何从提交表单自定义数据全日历包

    我在 FullCalendar 上方添加了一个选择表单来选择用户并显示他的事件 问题是如何加载日历中选择的用户的事件 这是一些代码 在 Calendar html twig 中 block javascripts parent
  • 如何使用 .c 文件而不是 .cpp 文件在 google test 中编写测试类?

    我已将 googletest 用于包含 c 文件的 Android NDK 项目 我使用了 cpp 类型的测试类来执行相同的操作 我想改用 c 文件 当我尝试使用它时出现以下错误 Running main from gtest main c
  • UIGraphicsBeginImageContext 与 CGBitmapContextCreate

    我正在尝试更改背景线程中图像的颜色 苹果文档说 UIGraphicsBeginImageContext 只能从主线程调用 我正在尝试使用 CGBitmapContextCreate 上下文 CGBitmapContextCreate 位图数
  • 如何在Ubuntu上安装OpenAL SDK?

    我对 Linux 和 Linux 编程非常陌生 我正在尝试在 ubuntu 上安装 OpenAL SDK 我最好的猜测是我需要从 CVS 存储库下载 OpenAL 我找到了一个教程 http www edenwaith com produc
  • 在 FormCreate() 中我可以做什么,不可以做什么?

    我认为这一定是一个常见问题解答 但谷歌搜索并没有真正帮助 我可以做什么 和不可以做什么 FormCreate 我想知道表单的所有子控件是否都已完全创建并可供访问等 我问的原因是我偶然发现了一个旧项目 我的FormCreate 简单地包括 S
  • 使用 sed 仅更改连续重复的字母

    使用 sed 如何将字母 a 更改为 A 但前提是它重复出现为两个或多个连续字母 示例 来自 galaxy ear aardvak Haaaaaaaaa into galaxy ear AArdvak HAAAAAAAAA 您可以使用组来完
  • 计算 Pandas GroupBy 对象中日期的差异

    我有一个具有以下格式的 Pandas DataFrame In 0 df Out 0 col1 col2 date 0 1 1 2015 01 01 1 1 2 2015 01 09 2 1 3 2015 01 10 3 2 1 2015
  • 脚本和matlab命令窗口之间的区别

    我想知道在命令窗口中输入几行或让脚本执行它们之间有什么区别 在问题中逃离嵌套的 try catch 语句我有一个示例功能 我已将选定的代码放入脚本中 然后叫它 但是这样它就无法正常工作 另一方面 当我选择行并按 f9 它按预期工作 这些行是
  • 如何在ParaView中读取Ansys数据文件?

    有人知道如何将 Ansys 结构数据文件导出到 ParaView 吗 据了解ParaView有一个Ansys阅读器 但它不起作用 加载 inp 文件时总是会出现错误 是否有任何脚本可以将 inp 文件转换为 vtk Thanks 基于vid
  • 对范围对象使用 Range.Sort 时 Excel for Mac 崩溃

    我有一个 Excel 电子表格 单元格 A1 到 A8 中有 8 个随机数 然后我有两个按钮 每个按钮运行一个 VBA 子程序 Sub Button1 Click Sheets Sheet1 Range A1 A8 Sort key1 Sh
  • SQL日期查询中的VBA变量

    我正在尝试查询 SQL 数据库中日期在用户输入给出的日期之后的所有行 当我用 包围日期时 我遇到了各种错误 从 附近语法不正确 到 将表达式转换为算术溢出错误 我当前的代码如下所示 inputdate InputBox Please ent
  • 增加谷歌云虚拟机的配额

    最近 我试图通过向我的虚拟机添加 GPU 来获得 Google 云加速器的好处 因此请求增加配额 因此我发送了 8 个 GPU 的请求 并收到了以下电子邮件 注意我尝试了多次 那么任何人都可以解释到底发生了什么以及如何成功增加 提前致谢 此
  • 在服务器上所有数据库的所有表中搜索字符串

    编辑 这个问题被标记为重复 但事实并非如此 SO 上的其他答案显示了如何搜索单个数据库中的所有表 我需要搜索给定服务器上每个数据库中的所有表 我需要在服务器上的所有数据库的所有表中搜索搜索字符串 我的电子邮件地址散布在即将更改域名的表格中
  • 本地存储未定义

    我正在尝试在 javascript 中使用 localstorage 在执行之间保存数据 这是使用它的代码 const commando require discord js commando roasts localStorage get
  • 当我使用 NetBeans 6.8 和 Eclipse 运行此代码时,为什么输出有所不同?

    当我使用 Eclipse 和 NetBeans 6 8 运行以下代码时 我想查看计算机上可用的 COM 端口 在 Eclipse 中运行时 它返回所有可用的 COM 端口 但在 NetBeans 中运行时 它似乎找不到任何端口 public
  • Swift 中的 NSNotificationCenter addObserver

    如何在 Swift 中将观察者添加到默认通知中心 我正在尝试移植这行代码 该代码行在电池电量变化时发送通知 NSNotificationCenter defaultCenter addObserver self selector selec
  • PHP 多个 cookie 无法在 iPad / iPhone 浏览器上运行

    所以我的应用程序是一个基于 iframe 的电子商务购物车 购物车会话存储在访问域中 当用户签出时 iframe 会部署一个弹出登录到我们的 SAAS 这会清除我们域的跨域 cookie 弹出登录经过身份验证后 弹出窗口关闭 iframe
  • 模态视图控制器关闭后 UITableView 不刷新

    我的 UITable 视图不会从数组的数据源刷新 该数组是由模态视图控制器编辑的 我已经确认编辑工作正常 我已经尝试过这些 void viewWillAppear BOOL animated myTableView reloadData N
  • EB AWS 上出现错误 - 502 错误网关 nginx/1.18.0

    我在 AWS 中运行的 Node 应用程序遇到问题 直到今天一切都运转良好 如果没有对应用程序进行任何新部署 当我尝试访问 URL 时会收到此 502 错误 我检查了 nginx 日志 得到了以下信息 2020 10 11 01 24 55
  • 如何在类型包装器(映射类型)中转发(众所周知的)符号?

    按照文档映射类型 在 TypeScript 中应该可以有这样的包装器 export type Wrapped