保留通用变量之间的类型限制

2023-12-03

假设我们有一些类有一个重要的泛型变量T另一个类有两个字段,一个包含,一个不包含:

class Wrapper<V> {
    constructor(public value: V) {
    }

    clone(): Wrapper<V> {
        return new Wrapper(this.value);
    }
}

class SomeClass {
    value1 = new Wrapper(1);
    value2 = 2;
}

然后,我们想要一个方法wrapperValue其中,当给定一个对象时(obj) 和字段名称 (name) 返回通过访问的包装器的值obj[name].value。返回类型正确非常重要。到目前为止,这是我设法想出的:

type WrapperKeyOf<S> = keyof {
  [K in keyof S as S[K] extends Wrapper<any> ? K: never]: any
}

type WrapperValueTypeOf<W> = W extends Wrapper<infer V> ? V : never;

function wrapperValue<S, K extends WrapperKeyOf<S>>(
    obj: S,
    name: K,
): WrapperValueTypeOf<S[K]> {
    const wrapper: Wrapper<WrapperValueTypeOf<S[K]>> = obj[name];
    return wrapper.value;
}

wrapperValue(new SomeClass(), "value1");

方式WrapperKeyOf限制name只作为钥匙S where S[T] is a Wrapper, and WrapperValueTypeOf<S[T]>获取包装类型。

TypeScript 编译器会产生以下错误:

Type 'S[K]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
  Type 'S[keyof { [K in keyof S as S[K] extends Wrapper<any> ? K : never]: any; }]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
    Type 'S[string] | S[number] | S[symbol]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
      Type 'S[string]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.

看来事实是这样的K必须是一个关键S它访问了一个Wrapper迷路。有什么办法可以以某种方式保存这些信息吗?


不幸的是编译器无法执行这种抽象generic需要进行类型分析来验证T[KeysMatching<T, V>]可分配给V对于通用的T, where KeysMatching<T, V>是属性键的并集T其属性值可分配给V,如中所述这个问题。问题是KeysMatching<T, V>只能用一个来实现条件类型(在那里的某个地方你会有一张支票,比如T[K] extends V ? K : never),并且编译器本质上将依赖于泛型类型参数的条件类型视为opaque,并选择defer对它们进行评估,直到用某种特定类型指定泛型类型参数。这实际上是 TypeScript 的设计限制,记录在微软/TypeScript#30728 and 微软/TypeScript#31275(可能还有其他)。

自从你的WrapperKeyOf<T>是一个实现KeysMatching<T, Wrapper<any>>,这意味着编译器看不到T[WrapperKeyOf<T>]将可分配给Wrapper<any>.

然而,编译器can告诉你,当你index into a 映射类型形式的{[P in K]: V}(或等效使用the Record<K, V>实用型)用钥匙K你会得到一些可分配给的东西V.

因此,如果您根据约束重新表述您的要求obj而不是限制name,您将能够获得您正在寻找的类型安全保证:

function wrapperValue<V, K extends PropertyKey>(
    obj: Record<K, Wrapper<V>>,
    name: K,
): V {
    const wrapper = obj[name];
    return wrapper.value; // okay
}

在这里我们让name属于泛型类型K可以是任何类似键的类型,然后我们限制obj成为有钥匙的东西K并且该键的值是类型Wrapper<V>对于泛型类型参数V。现在编译器知道obj[name].value属于类型V,所以执行是没有错误的。

还有您的来电wrapperValue()仍然是安全的(尽管当你犯错时,错误现在会出现obj代替name):

const result = wrapperValue(new SomeClass(), "value1"); // okay
console.log(result.toFixed(1)); // 1.0

wrapperValue(new SomeClass(), "value2"); // error!
// --------> ~~~~~~~~~~~~~~~
// Type 'number' is not assignable to type 'Wrapper<number>'

wrapperValue(new SomeClass(), "value3"); // error!
// --------> ~~~~~~~~~~~~~~~
//  Property 'value3' is missing in type 'SomeClass'

Playground 代码链接

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

保留通用变量之间的类型限制 的相关文章

随机推荐

  • Android 轮播图库

    我怎样才能轮播图片库 它永远不会结束 在最后一张图片之后和第一张最后一张图片之前 我通过创建自己的列表适配器 继承自 基础适配器 我以 getCount 方法的方式编写了自己的列表适配器 返回一个 HUUUUGE 数字 如果选择 x 项 则
  • 没有 CocoaPods 的 Google 登录

    我正在尝试将 iOS 版 Google 登录与此处找到的文档集成 https developers google com identity sign in ios start integrating 在不使用 Cocoapods 的情况下如
  • 从 List 到数组 T[] 的转换

    有没有一种简单的方法来转换强类型List
  • PDFBox LayerUtility - 将图层导入现有 PDF

    我正在使用 pdfbox 来操作 PDF 内容 我有一个很大的 PDF 文件 比如 500 页 我还有其他一些仅包含单个图像的单页 PDF 文件 每个文件最大约为 8 15kb 我需要做的是将这些单页 pdf 导入到大 PDF 文件的某些页
  • 为什么 SQL Server 2014 数据库项目的 SqlPublish 目标失败?

    我的计算机具有当前 2014 年 3 月 版本的 SSDT Visual Studio 2012 Professional 和 SQL Server 2014 Developer 我有一个 SQL Server 项目 我们称之为MyProj
  • 克隆对象并更改值也会改变原始对象

    我想知道为什么会发生这种情况 我有一个对象存储在 var myObj 中 var myObj JSON parse fs readFileSync json data json utf8 然后我通过以下方式从原始对象中获取克隆 var mo
  • 如何使div的百分比宽度相对于父div而不是视口

    这是我正在使用的 HTML div style min width 100 min height 1000px background 3e3e3e div style width 50 height 1 div style backgrou
  • 初始化未知大小的二维数组

    我有一个二维字符数组 例如char aList numStrings maxLength 理想情况下 在程序执行期间 我希望能够修改 aList 的内容 即添加 修改或删除条目 由于 aList 可能会发生更改 因此我不想在每次更改后都必须
  • Android Smack 消息事件监听器

    我正在尝试使用 XMPP 的消息事件接口 据我了解 您可以在发送的消息上标记 请求送达通知 标志 然后收件人负责向您发送此通知 有人成功实施过这个吗 有人可以给我发送一些示例代码吗 我的代码不起作用 我的侦听器 MessageEventNo
  • Android 应用程序,文档中根元素后面的标记必须格式正确

    我已经开始从下面的链接模块 6 1 项目开始处理一些 Android 教程演示http www vogella de articles Android article html 对于下面的 menu xml 中的菜单标签 我在 Eclips
  • NSLinguisticTagger enumerateTagsInRange 不适用于具有 NSLinguisticTagSchemeNameTypeOrLexicalClass 的设备

    这是我正在使用的代码 无论我在设备上使用什么句子 它都不会打印任何内容 在模拟器上运行得很好 NSMutableArray getTagEntries NSString sentence NSArray
  • C# 字符串到浮点转换无效?

    var x dr NationalTotal ToString 给我 333333333 var xxx Convert ToSingle dr NationalTotal ToString 给我 333333344 有什么想法吗 发生这种
  • Firefox 插件忽略 iframe

    我正在尝试为 LinkedIn 构建一个插件 但内容脚本会在每一帧中输出 我的 main js exports main function var pageMod require page mod pageMod PageMod inclu
  • 使用 afterFeature 钩子调用时动态场景冻结

    当我使用 afterFeature 挂钩调用功能文件进行测试清理时 出现奇怪的行为 清理功能文件被正确调用 因为我可以看到文件背景部分的打印 但由于某种原因 场景大纲的执行挂起 我尝试使用 Junit5 运行程序运行功能 并在 Intell
  • TOMCAT - HTTP 状态 404 [重复]

    这个问题在这里已经有答案了 我在 eclipse 中设置了我的服务器 当我运行它时 控制台打印 mai 02 2013 4 05 13 PM org apache catalina core AprLifecycleListener ini
  • 无法使用角度材料显示角度 2 中垫表中的选定行

    我使用带选择框的角度材料在角度 2 中实现了一个简单的表格 当我选择所需的行并单击 传输所选行 时 这些行将被拼接 并且拼接的行将显示在我的控制台中 但我无法在输出窗口中显示它们 请通过链接访问样本示例 我想在我的输出屏幕上显示控制台输出
  • 如何设置 try-catch 以确保用户输入枚举列表中包含的值?

    我有一个星期几的枚举列表 以及一个要求用户输入当前日期的程序 可以是列表中的任何一天 我需要实现一个 try catch 以确保用户输入有效的日期 但我遇到了一些困惑 当我搜索如何使用枚举执行异常时 大多数网站建议使用 TryParse 而
  • 如何验证 Codeigniter 中组合的多个字段?

    有没有一种创造性且简单的方法可以同时检查多个表单字段 我有一个带有动态生成字段的表单 每个字段都有一个唯一的 ID 问题是提交时不需要填写所有字段 但在提交之前至少必须填写一个字段 有没有办法在 Codeigniter 中做到这一点 或者我
  • 不存在 ID 为 *id* 的消息

    我正在尝试列出通过 mandrill API 发送的电子邮件 我可以通过打电话列出它们https mandrillapp com api 1 0 messages search json 这给了我一个列表 其中包含一堆已发送的消息以及一个
  • 保留通用变量之间的类型限制

    假设我们有一些类有一个重要的泛型变量T另一个类有两个字段 一个包含 一个不包含 class Wrapper