如何通过 TypeScript 中的类型来描述元组数组中元组元素之间的关系?

2024-03-19

我想通过 TypeScript 中的类型来描述元组数组中元组元素之间的关系。

这可能吗?

declare const str: string;
declare const num: number;
function acceptString(str: string) { }
function acceptNumber(num: number) { }

const arr /*: ??? */ = [
    [str, acceptString],
    [num, acceptNumber],
    [str, acceptNumber], // should be error
];

for (const pair of arr) {
    const [arg, func] = pair;
    func(arg); // should be no error
}

TypeScript Playground 链接 https://www.typescriptlang.org/play/index.html#src=%2F%2F%20enable%20all%20strict%20flags%0D%0A%0D%0Adeclare%20const%20str%3A%20string%3B%0D%0Adeclare%20const%20num%3A%20number%3B%0D%0Afunction%20acceptString(str%3A%20string)%20%7B%20%7D%0D%0Afunction%20acceptNumber(num%3A%20number)%20%7B%20%7D%0D%0A%0D%0Aconst%20arr%20%2F*%3A%20%3F%3F%3F%20*%2F%20%3D%20%5B%0D%0A%20%20%20%20%5Bstr%2C%20acceptString%5D%2C%0D%0A%20%20%20%20%5Bnum%2C%20acceptNumber%5D%2C%0D%0A%20%20%20%20%5Bstr%2C%20acceptNumber%5D%2C%20%2F%2F%20should%20be%20error%0D%0A%5D%3B%0D%0A%0D%0Afor%20(const%20pair%20of%20arr)%20%7B%0D%0A%20%20%20%20const%20%5Barg%2C%20func%5D%20%3D%20pair%3B%0D%0A%20%20%20%20func(arg)%3B%20%2F%2F%20should%20be%20no%20error%0D%0A%7D

现实世界的例子:TypeScript Playground 链接 https://www.typescriptlang.org/play/index.html#src=enum%20Type%20%7B%0D%0A%20%20%20%20Value1%2C%0D%0A%20%20%20%20Value2%2C%0D%0A%7D%0D%0A%0D%0Aclass%20Store1%20%7B%0D%0A%20%20%20%20a%3A%20any%3B%0D%0A%7D%0D%0A%0D%0Aclass%20Store2%20%7B%0D%0A%20%20%20%20b%3A%20any%3B%0D%0A%7D%0D%0A%0D%0Aclass%20Form1%20%7B%0D%0A%20%20%20%20constructor(store%3A%20Store1)%20%7B%20%7D%0D%0A%7D%0D%0A%0D%0Aclass%20Form2%20%7B%0D%0A%20%20%20%20constructor(store%3A%20Store2)%20%7B%20%7D%0D%0A%7D%0D%0A%0D%0Aconst%20map%20%3D%20%7B%0D%0A%20%20%20%20%5BType.Value1%5D%3A%20%5BStore1%2C%20Form1%5D%2C%0D%0A%20%20%20%20%5BType.Value2%5D%3A%20%5BStore2%2C%20Form2%5D%2C%0D%0A%7D%3B%0D%0A%0D%0Afunction%20createForm(type%3A%20Type)%20%7B%0D%0A%20%20%20%20const%20%5BStore%2C%20Form%5D%20%3D%20map%5Btype%5D%3B%0D%0A%20%20%20%20return%20new%20Form(new%20Store())%3B%0D%0A%7D%0D%0A


你基本上是在询问我一直在打电话的事情相关记录类型 https://github.com/Microsoft/TypeScript/issues/30581,目前 TypeScript 中没有直接支持。即使您可以说服编译器在创建此类记录时捕获错误,但在使用记录时它并没有真正具备验证类型安全性的能力。

实现此类类型的一种方法是存在量化的泛型 https://en.wikipedia.org/wiki/Type_system#Existential_types哪个 TypeScript目前不直接支持 https://github.com/Microsoft/TypeScript/issues/14466。如果是这样,您就可以将您的数组描述为:

type MyRecord<T> = [T, (arg: T)=>void];
type SomeMyRecord = <exists T> MyRecord<T>; // this is not valid syntax
type ArrayOfMyRecords = Array<SomeMyRecord>;

下一个最好的事情可能是允许ArrayOfMyRecords它本身是一个泛型类型,其中数组的每个元素都与其类似的元素强类型化T值,以及用于推断更强类型的辅助函数:

type MyRecord<T> = [T, (arg: T) => void];

const asMyRecordArray = <A extends any[]>(
  a: { [I in keyof A]: MyRecord<A[I]> } | []
) => a as { [I in keyof A]: MyRecord<A[I]> };

这使用从映射类型推断 https://www.typescriptlang.org/docs/handbook/advanced-types.html#inference-from-mapped-types and 映射元组 https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#mapped-types-on-tuples-and-arrays。让我们看看它的实际效果:

const arr = asMyRecordArray([
  [str, acceptString],
  [num, acceptNumber],
  [str, acceptNumber] // error
]);
// inferred type of arr:
// const arr:  [
//   [string, (arg: string) => void], 
//   [number, (arg: number) => void], 
//   [string, (arg: string) => void]
// ]

让我们解决这个问题:

const arr = asMyRecordArray([
  [str, acceptString],
  [num, acceptNumber],
  [str, acceptString] 
]);
// inferred type of arr:
// const arr:  [
//   [string, (arg: string) => void], 
//   [number, (arg: number) => void], 
//   [string, (arg: string) => void]
// ]

所以这足以定义arr。但现在看看当你迭代它时会发生什么:

// TS3.3+ behavior
for (const pair of arr) {
  const [arg, func] = pair; 
  func(arg); // still error!
}

这就是缺乏对相关记录的支持让你烦恼的地方。在 TypeScript 3.3 中,添加了对调用的支持函数类型的并集 https://github.com/microsoft/TypeScript/pull/29011,但该支持并没有触及这个问题,即:编译器将func as a union的功能是完全不相关的与类型arg。当您调用它时,编译器决定它只能安全​​地接受类型的参数string & number, which arg不是(也不是任何实际值,因为string & number折叠到never).

所以如果你走这条路你会发现你需要一个类型断言 https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions让编译器平静下来:

for (const pair of arr) {
    const [arg, func] = pair as MyRecord<string | number>;
    func(arg); // no error now
    func(12345); // no error here either, so not safe
}

人们可能会认为这是你能做的最好的事情并将其留在那里。


现在,有一种方法可以在 TypeScript 中对存在类型进行编码,但它涉及到Promise-类似控制反转。不过,在我们走这条路之前,问问自己:你实际上打算做什么do with a MyRecord<T>当你不知道的时候T?您可以做的唯一合理的事情是用第二个元素调用它的第一个元素。如果是这样,你可以给出一个更具体的方法这样做不跟踪T:

type MyRecord<T> = [T, (arg: T) => void];
type MyUsefulRecord<T> = MyRecord<T> & { callFuncWithArg(): void };

function makeUseful<T>(arg: MyRecord<T>): MyUsefulRecord<T> {
    return Object.assign(arg, { callFuncWithArg: () => arg[1](arg[0]) });
}

const asMyUsefulRecordArray = <A extends any[]>(
    a: { [I in keyof A]: MyUsefulRecord<A[I]> } | []
) => a as { [I in keyof A]: MyUsefulRecord<A[I]> };

const arr = asMyUsefulRecordArray([
    makeUseful([str, acceptString]),
    makeUseful([num, acceptNumber]),
    makeUseful([str, acceptString])
]);

for (const pair of arr) {
    pair.callFuncWithArg(); // okay!
}

您的现实世界示例可以进行类似修改:

function creatify<T, U>(arg: [new () => T, new (x: T) => U]) {
    return Object.assign(arg, { create: () => new arg[1](new arg[0]()) });
}

const map = {
    [Type.Value1]: creatify([Store1, Form1]),
    [Type.Value2]: creatify([Store2, Form2])
};

function createForm(type: Type) {
    return map[type].create();
}

在 TypeScript 中模拟存在类型与上面的类似,除了它允许你对一个对象做任何事情。MyRecord<T>如果你不知道的话可以这样做T。由于在大多数情况下,这是一小组操作,因此直接支持这些操作通常更容易。


好的,希望有帮助。祝你好运!

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

如何通过 TypeScript 中的类型来描述元组数组中元组元素之间的关系? 的相关文章

  • 如何在 TypeScript 中基于只读元组动态创建对象?

    在 TypeScript 中 我可以使用以下构造基于只读元组定义类型的属性 const keys A B C as const type T key in typeof keys number boolean 这的行为符合预期 const
  • FP-TS 分支(面向铁路的编程)

    我在尝试使用 FP TS 实现事物时不断遇到的一种模式是 当我的管道涉及到 TaskEither 的分支和合并分支时 合并似乎工作得很好 因为我可以使用sequenceT创建数组并将它们通过管道传输到函数中 然后使用所有这些值 似乎不太有效
  • 使用带有十六进制字符串的 CryptoJS

    我想连接到蓝牙设备 仅通过十六进制字符串进行通信 我需要编码一个 16 字节值 因此我也期望一个 16 字节的值 在我的实现中 CryptoJS 总是返回更长的结果 根据文档 不需要 IV 所有 16 字节数据必须使用当前存储在设备中的客户
  • 用于测试对象类型的通用 Swift 函数

    我正在尝试编写一个函数 该函数接受一个对象和一个类型作为参数 并返回一个布尔值 指示该对象是否属于给定类型 似乎没有 Type 类型 所以我不知道如何做到这一点 我能做的最好的就是 func objectIsType
  • viewChild如何获取Angular2中添加了js的元素?

    如果一个 HTML 元素已添加到 DOM 中 例如单击按钮后 我们如何访问该元素 viewChild 看不到它 更新1 更多说明 我用过 jquery 数据表 jquery dataTablesdefineTyped 版本 https gi
  • 在模块内调用全局变量

    我有一个名为的打字稿文件Projects ts我想引用一个名为的引导插件中声明的全局变量bootbox js 我想访问一个名为bootbox从 TypeScript 类中 是否可以 您需要告诉编译器它已被声明 declare var boo
  • 如何在 WebStorm 中“运行”TypeScript 文件?

    在我的 WebStorm IDE 中 所有 js and jsx 我的项目中的文件有一个关联的 运行 命令 但此功能不存在 ts or tsx files 我相信我已经为 TypeScript 正确配置了我的项目 因为我至少能够运行tsc从
  • 如何连接我的 angular2 应用程序 javascript 文件

    对于我的 Angular2 TypeScript 应用程序 我将所有 js 文件合并到一个 app min js 文件中 然后 System import 将此文件导入到我的 index html 页面 然后我得到一个同一模块文件中的多个匿
  • 类型不可分配给映射类型

    在下面的例子中我得到Type string is not assignable to type MapType
  • 如何获取与 Node.js 中的 TypeScript 文件行数相关的错误信息?

    我正在使用 TypeScript 进行 Node js 后端开发 每当我在 node js 中遇到错误时 它都会显示与已转译的 JavaScript js 文件相关的行号 而不是与 TypeScript ts 文件相关的行号 如果您使用了
  • 在 swift 中使用协议作为数组类型和函数参数

    我想创建一个可以存储符合某种协议的对象的类 对象应该存储在类型数组中 根据 Swift 文档 协议可以用作类型 因为它是一种类型 所以您可以在许多允许其他类型的地方使用协议 包括 作为函数 方法或初始值设定项中的参数类型或返回类型 作为常量
  • 在 Swift 中使用模板键入别名声明

    如何避免函数中多余的限制声明f0 f1 f10 class SomeClass
  • 基于字符串的动态返回类型

    是否可以将字符串传递给函数并分配返回的动态类型 class MyService aMethod arg console log arg Container register my service MyService Internally C
  • 将 Angular v12 升级到 v13 时出现“模块未找到”错误

    嗨 开发者和贡献者 我正在努力找出以下错误的问题所在 src app models type ModelType ts 2 0 44 错误 找不到模块 错误 导出字段无法解析目录 请求为 当我将 Angular 版本从 v12 升级到 v1
  • Material UI 选择覆盖主题中的位置

    我想覆盖主题中选择字段下拉列表的位置 不必在每个选择上实现它 我尝试过 createMuiTheme overrides MuiSelect select MenuProps getContentAnchorEl null anchorOr
  • 定义文件:属性的多种可能类型

    我正在为现有的 JS 库 CKEditor 编写一些定义 是否可以更具体toolbar any 文档 工具栏 数组 字符串 工具箱 别名工具栏 定义 它是工具栏名称或 工具栏 条 数组 每个工具栏也是一个数组 包含一个 UI 项目列表 库代
  • 修改对象实例的 TypeScript 类装饰器

    我正在为 Aurelia 制作一个插件 需要一个类装饰器 将属性添加到新对象实例 并且 使用新对象作为参数调用外部函数 我已经查看了示例 到目前为止我已经将它们放在一起 伪 代码 return function addAndCall tar
  • 类型错误:无法读取未定义的属性“post”

    嗨 我只想使用一个简单的功能 http post 将我的日期发布到页面 我希望服务器能够获取我发布的日期 import Component OnInit from angular core import MarginServcies fro
  • 扩展中的 Swift 覆盖函数

    如果我有课 class Spaceship
  • 在 Angular 中导入和使用 lodash 的正确方法

    我曾经能够通过如下所示的 import 语句在 Angular 中使用 lodash 方法 import debounce as debounce from lodash 我现在在使用该语句时收到以下错误 node modules type

随机推荐

  • Google Chrome 开发工具无法在 Elements Tag 中显示正文内容

    大家都有这样的情况吗 Chrome开发工具Elements标签下 body标签的内容无法显示 唯一的方法是关闭开发工具并重新打开 这是一个 Chrome 错误 在 Canary 中仍然发生 crbug com 829884 https bu
  • ggplot geom_bar 的比例[重复]

    这个问题在这里已经有答案了 使用 ggplot 最简单的方法是什么 与此处相同 我需要调用 prop table 还是有更简单的方法 可重现的示例 x lt c good good bad bad bad bad perfect perfe
  • 无法定位到动态链接库

    我在读取 C 中的文本文件时遇到问题 所以基本上我想使用此代码读取 cmd 上的文本文件 但是会弹出错误 include
  • 如何从内容页使用母版页中的方法

    我正在使用 C 编写 ASP NET 4 应用程序 我有一个母版页 其中有以下方法 public void DisplayMessage string input Label myMessageDisplayer Label FindCon
  • 更改 x 轴上的刻度

    我正在尝试找出 d3 js 定义轴时 如何在 x 轴上获得自定义标签 例如 我得到的默认标签是 20 30 40 50 60 70 80 然而 我想要这样的东西 20 26 32 38 44 50 56 我目前正在学习它并根据官方提供的示例
  • 用线性渐变制作CSS3三角形

    我需要创建一侧带有三角形的按钮 像这样http css tricks com triangle breadcrumbs 具有线性垂直渐变和边框 我想使用纯CSS3 如果我需要45度的 三角形 就可以了 我就这样写 button after
  • 使用循环在rmarkdown中生成一段文本

    我需要生成一份由多个部分组成的报告 所有部分看起来都很相似 只有一些数据差异 部分的数量也取决于数据 我最终想要拥有的是这样的 r section names c A B C section data c 13 14 16 some loo
  • 使用C#登录https站点

    我正在尝试编写一个小程序 用于登录 Verizon 网站 然后检查该月还剩多少分钟 我需要帮助了解如何使用 C 登录该网站 我知道我需要使用 webrequest 来发布登录信息 但我不知道如何去做 带有登录表单的网站是https logi
  • Django 使用整数参数(主键)从模板构建 URL

    我在模板中有这个链接 a href Item 1 a 以及 urls py 中的这个 url url r item P
  • textinputlayout 密码切换图标被阻止

    我不知道如何用文字解释 但让附图来说话吧 基本上 setError 图标挡住了密码切换图标 最初 我认为这是一个简单的布局问题 我尝试了颜色和背景等 但是 经过多次尝试和错误 我似乎找不到问题的解决方案 我将在这里发布我的 XML 供您参考
  • jquery:window.location.reload() 不允许工作 $.post()

    请看看这个脚本 change click function var val new title val if val alert return false else post change title php id id lang lang
  • 如何通过ajax将mysql结果作为json传递

    我不知道如何通过 ajax JSON 将 mysql 查询的结果传递到 html 页面 ajax2 php statement pdo gt prepare SELECT FROM posts WHERE subid IN key2 AND
  • 在 Yii2 中运行行为代码之前获取控制器操作

    我正在尝试在 a 中执行一些代码Yii2控制器 因为我需要模型中的一些代码才能在behaviors部分 以便我可以将模型作为参数传递并避免运行重复的查询 但是我还需要能够找出什么action正在被召唤 但我运气不太好 我尝试过使用befor
  • 如何强制 DIV 向下延伸到屏幕底部?

    这对于 CSS 来说可能是不可能的 但也许我错了 我有这样的文档结构 BODY DIV A DIV B DIV A 是position absolute具有固定宽度并在屏幕上居中 它没有高度设置 DIV B 是position absolu
  • 从http下载SSIS - 错误从服务器获得的SSL认证响应无效

    我创建了一个 SSIS 包 它使用 C 脚本从 HTTPS URL 下载 CSV 文件 从 Visual Studio 执行时一切正常 但是 当我创建 SQL 代理作业时 包失败 如果我直接从 SQL Server 执行 dtsx 文件 该
  • 从字符串创建 IPEndpoint 的最佳方法

    Since IPEndpoint包含一个ToString 输出的方法 10 10 10 10 1010 还应该有Parse and or TryParse 方法但是没有 我可以将字符串分开 并解析 IP 地址和端口 但有没有更优雅的方式呢
  • Scala - 从字符串中删除除链接之外的所有 html 标签

    我正在尝试使用 scala 中此线程上找到的正则表达式模式 去除除链接之外的所有 HTML 标签 https stackoverflow com questions 44078 strip all html tags except link
  • 如何在 Paypal 中设置定期付款和一次性付款的混合

    我正在使用 Paypal REST API 来收取我的订阅费用 我的交易包括一次性购买和定期付款的混合 一次性购买将向用户收取 X 金额 定期付款应创建定期配置文件 每年向用户收取 Y 金额 这是我所拥有的 首先我打电话SetExpress
  • 使用Python分割数据块时出错

    我有一个解析过的文件 我需要根据LogType 以下是我的数据 LogType container localizer syslog Log Upload Time Thu Jun 25 12 24 45 0100 2020 LogLeng
  • 如何通过 TypeScript 中的类型来描述元组数组中元组元素之间的关系?

    我想通过 TypeScript 中的类型来描述元组数组中元组元素之间的关系 这可能吗 declare const str string declare const num number function acceptString str s