带有一般错误的 Typescript 缩小类型

2023-12-22

有人可以帮我理解这里发生了什么吗:TS游乐场 https://www.typescriptlang.org/play?#code/C4TwDgpgBACghgJzgWwDwHkzAJYHsB2AzgHxQC8UA3gLABQUUA2gNJTb5QDWEIuAZlEw4ChALoAuKACUIcACYEANiFQ16DNnMnMANHQ1RcWPEUlCThFqL3qAvsTq3G3XgPMjRAbjp12wCAh8cADG0ADKwLgIEBjGIqRqBhAAHhDBkgAU+CnAEXD+kvBIaO5ExACU5KQAbrjYco4+tKCQsIgQ+MClhORU+hoA5HADkpRQcJKEwAjsAOZQtja2UABkUGEArgBG3U0t4dvdvYmDWyNUUFuS+BvIWwELS018G-jBwhz+Uxn9DGDtnQAYopcAB3ACiqXSUCyOTyBTaxVQRQ6XTiZUqZBqdTkNgMhG2wLBkLSmWyyVywHyEEKiBQqE2O3RJEx2PqeI0vyg-2inQAcgQAOIdALYaFjFKkmHkynU2lIlGdboVKpQWr1R5cglbAX4YXZGbiqCS6GwinwmmI+mM5WstU4zXqBhcnmoomgyQRKIxRVoj4kDkMbXuz2RaIMw7M4iBqB0SonP4A4DuknBXrBkEQqGeKAAelzxoQCCiklwnC1hMzqd6rqBVezeYL-0IhAgDVoLqTuv1orTFG13ZFhpz+cLxYQpfLTqgA6FQ7FNa7c4NYpHTbgLbbTQMteTmfTlbBa7HJcMU-xh9Bi95e6PjZPCCgAH4n9uNKOAJLcxRwT64KAbK2bDAFAijYNwUDAAAFtghAVlsMDFqELYZLu7rlN4tC2M8rzvCYM7bIhuDIYQGR8JmXrRKG3oRky-oqgmUDkWClEQAAdCaGRjPUkhDAMOiGMyozjLxwwLAsGH3sEv4DCB0mKIoUCgtg0FQHxXLMaCrEcVCXGaLxZwCUY-rCVcUAAMzibYkmjmWjhAA

基本上我有一个store有一个exec方法,我想缩小类型exec子进程的参数 但商店类型是泛型类型似乎有错误

type Param<Options> = {
  [K in keyof Options]: Readonly<{
    id: K,
    options: Options[K],
  }>
}[keyof Options];

interface Store<Options> {
    exec: (nextState: Param<Options>) => void
}

type ParentOptions = {
    'a': { a: string },
} & SubOptions

type SubOptions = {
    'b': { b: number },
}

function test(
    parentFlowExec: (nextState: Param<ParentOptions>) => void,
    subFlowExec: (nextState: Param<SubOptions>) => void,
    
    parentNonGeneric: { exec: (nextState: Param<ParentOptions>) => void },
    subNonGeneric: { exec: (nextState: Param<SubOptions>) => void },
    
    parentFlow: Store<ParentOptions>,
    subFlow: Store<SubOptions>,
    
) {
    parentFlowExec = subFlowExec; // error: ok
    subFlowExec = parentFlowExec; // passed

    parentNonGeneric = subNonGeneric; // error: ok
    subNonGeneric = parentNonGeneric; // passed

    parentFlow = subFlow; // error: ok
    subFlow = parentFlow; // error ??

    // I plan to use it like this
    subProcess(parentFlow);
}

function subProcess(flowStore: Store<SubOptions>) {
    flowStore.exec({ id: 'a', options: { a: 'a' } }); // can't call with 'a'
    flowStore.exec({ id: 'b', options: { b: 3 } }); // ok
}

更新: 我将 Param 移出并已它工作 https://www.typescriptlang.org/play?#code/C4TwDgpgBACghgJzgWwDwHkzAJYHsB2AzgHxQC8UA3gLABQUUA2gNJTb5QDWEIuAZlEw4ChALoAuKACUIcACYEANiFQ16DNnMnMANHQ1RcWPEUlCThFqL3qAvsTq3G3XgPMjRAbjp12wCAh8cADG0ADKwLgIEBjGIqRqBhAAHhDBkgAU%20CnAEXD%20kvBIaO5ExACU5KQAbrjYco4%20tH4BQaFQEVEQAEyxwmVU%20hopaZnZybnA%20RBmcUSVZDV1DbS2TaCQsIgQ%20MClhOSD6gwA5HAnkpRQcJKEwAjsAOZQtja2UABkHQCuAEb763A4T%20%200OiQ0J1%20FyoUF%20knw32QvwCLzeTT433wwX6UH8dwyQwYYG2uwAYopcAB3ACiqXSUCyOTyBS2xVQRR2ezmJAWS3qNgMhD%205KptNGDPGk2mhUQKFQYRB3IqVSgtX5hKgGuJ0V2ADkCABxHYBbD0q4jemMibMmasuUc3b7ZWLVXLVEaoW-fX4I3ZB5mqAWsZMqYsopyhX-JW8131d3HTUJ7WckWUySdaLsklc-okAUaT2p9ORTORp35hha7PdYtdXrhtAOnMWYjECtQT01jolnpZtllpVtuiVcFE7OpsXBQ6Fik0umeKAAekXgYQCCiklwnA9wtnk8OybJe-nS5XxMIhAgKyrOuA3t9JqnFE99%20N-oXy9X64Qm%203CZfhpvqaB7Zq%20fqmh%20Z5wBeV5NAYh7AKm067lSkFfhuhh-oKKGUiBt6pmhATflAAD8JFwRoCHdMhvzdN4-5-NRFBUfRGqfgAklAYCKHAHCRFA3yXmwwBQIo2DcLiAAW2CEDuvwwOuoQXhkCGpuU9EGGxK6UlEnBPHJCm4EphDdCp1bqY0tB0BiWI4p6hnGRkfCzhmtqufKiq5sqo5QM5VKuQAdBaGRXPUkhnCcOiGNylzXOF5wvC86mnlAwS8ScIlpYoihQJS2DAJJUARRqfmUoFwWhVoRVQlFRi5rFcJQAAzIltjJZ%20W6WTZ2ImB2fwORAF6maVrm1tE9aymgA5ed5JUuT2QV0iFmjxZF0X1TCNxFQl7xtWhaX4BlqVwNluX5YVxUJiNC0VSt1VrXVFgNZILW7e1K6dasQA但仍然不明白为什么嵌套它们不起作用

interface Store<Options> {
    exec: (nextState: Options) => void
}
// parent2: Store2<Param<ParentOptions>>,
// sub2: Store2<Param<SubOptions>>,

为了回答您的问题,首先,让我们快速回顾一下不同的“方差”的含义。在下表中,我使用 Microsoft 的定义.NET 文档 https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance(除了文档中没有的双变量),因为我发现它们最容易掌握:

Variance Meaning Allowed substitutions
Bivariance Covariance and Contravariance at the same time Supertype -> Subtype, Subtype -> Supertype
Covariance Enables you to use a more derived type than originally specified Supertype -> Subtype
Contravariance Enables you to use a less derived type than originally specified Subtype -> Supertype
Invariance Means that you can use only the type originally specified none

让我们检查一下哪一种类型是超类型,哪一种是子类型:

type T1 = SubOptions extends ParentOptions ? true : false; // false
type T2 = ParentOptions extends SubOptions ? true : false; // true

由此可见,PartentOptions是一个子类型SubOptions,而后者是它的超类型。它告诉我们什么?它告诉我们当你注释时subFlow as Store<SubOptions>然后尝试分配parentFlow到它(注释为Store<ParentOptions>),您正在尝试分配一个需要父类型的子类型.

如果我们参考方差表,我们会发现这需要协方差,但是当您收到错误时,这意味着我们正在处理逆变 or 不变性。现在,当您分配subFlow to parentFlow,您正在分配一个需要子类型的超类型.

上面也会导致错误,这意味着这里的赋值实际上是不变的, and @船长约塞连 https://stackoverflow.com/users/8495254/captain-yossarian's comment https://stackoverflow.com/questions/67482793/typescript-narrowing-type-with-generic-error#comment119279210_67482793是正确的:

我相信这是因为 subFlow 和parentFlow 彼此不变。

然而,这种行为是 TypeScript 的设计限制(参见 Anders Hejlsberg 的comment https://github.com/microsoft/TypeScript/issues/32674#issuecomment-716178615在相关问题上)为了健全性牺牲了一些灵活性(删除[keyof Options]索引,您将看到逆变赋值成为可能)。

As for your solution, due to how variance analysis works, when you move the Params outwards, the parameter types become covariant (as T[keyof T] is not aliased here. Note that when reduced to the bare structure, the Param type is exactly that: type Param<Options> = Options[keyof Options], only mapped1).

Take a look at a simplified example0 of your solution:

type Param<Options> = {
  [K in keyof Options]: Readonly<{
    id: K,
    options: Options[K],
  }>
}[keyof Options];

interface Store<Options> {
    exec: (nextState: Options) => void
}

type SuperOptions = { 'b': { b: number } }
type SubOptions = { 'a': { a: string } } & SuperOptions

const test1 = (subtype: Store<Param<SubOptions>>) => subProcess1(subtype); // OK, Subtype -> Supertype, covariance
const test2 = (supertype: Store<Param<SuperOptions>>) => subProcess2(supertype); // error, Supertype -> Subtype, contravariance

const subProcess1 = (supertype: Store<Param<SuperOptions>>) => supertype.exec({ id: 'b', options: { b: 3 } }); // ok
const subProcess2 = (subtype: Store<Param<SubOptions>>) => subtype.exec({ id: 'b', options: { b: 3 } }); // ok

操场 https://www.typescriptlang.org/play?#code/C4TwDgpgBACghgJzgWwDwHkzAJYHsB2AzgHxQC8UA3gLABQUUA2gNJTb5QDWEIuAZlEw4ChALoAuKACUIcACYEANiFQ16DNnMnMANHQ1RcWPEUlCThFqL3qAvsTq3G3XgPMjRAbjp12wCAh8cADG0ADKwLgIEBjGIqRqBhAAHhDBkgAU+CnAEXD+ZnFEAJTkpABuuNhyjj60oJBQYQCukAjuRORUUADkAEY9kpRQfZL4zch9AVC2M3QN4c19HYRdwz1wg91wkoTACOwA5jMnAGRNrQErdcEiwFD+ewCMXRmESwuSEVEx8EhoLWWRRIxFKZFI7z6MAQuFChEITzeH3AEGKnigAHoMYJdBc+gsoABaUgtNoLHRQW7lRDYOD4UJ0W5Ee6PYAAJle7zJKK+kWiqD+KFQpKuwOIoLKUEh0NhEHhbKR3MgaMx2ICMIQFJFCAJxLx5MpBH2cGpBzpDNojLuUqWMrhCM5lx1PKafN+iCF2pW4rBEKdCwAdCk0hlhtVJP0ehSjMJTN1RlAAMwnWwqrGGThW5k2qEw+0cihI-Eu778wUApbeiXgnOB4PBUOaCMDaPAoYjSTJ2ap9Hp3CcIA


0 Your naming choice slightly adds confusion to an already tough problem: a subtype is called ParentOptions and supertype SubOptions, while the relationship between them is the opposite, so I named them SubOptions and SuperOptions accordingly to make things clearer.

1 From the discussion in comments, it must be noted that while the relationship between Store<Param<SubOptions>> and Store<Param<SuperOptions>> in the solution is covariant, T[keyof T] here is contravariant (see Anders's comment https://github.com/microsoft/TypeScript/issues/32674#issuecomment-716192565 - the SuperOptions supertype has fewer properties than the SubOptions subtype, and there is no discriminant).

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

带有一般错误的 Typescript 缩小类型 的相关文章

随机推荐

  • Node-GYP 失败,退出代码:1

    我必须在 electro atom 中使用 node usb 来使用我正在尝试安装 node gyp 并且我面临一个问题 gyp ERR stack Error gyp failed with exit code 1 gyp ERR sta
  • 如何禁用 Android 按钮?

    我创建了一个包含两个按钮的布局 下一步 和 上一步 在按钮之间我生成一些动态视图 因此 当我第一次启动应用程序时 我想禁用 上一个 按钮 因为不会有任何以前的视图 当没有更多视图可显示时 我还想禁用 下一步 按钮 有没有办法禁用按钮 你尝试
  • 未找到 SDK 位置。使用 local.properties 文件中的 sdk.dir 或 ANDROID_HOME 环境变量定义位置

    我最近尝试导入从 Google 开发者网站下载的示例 Android 游戏 将它们导入 Android Studio 后 出现以下错误 错误 找不到 SDK 位置 定义位置sdk dir在 local properties 文件中或使用AN
  • imagemagick wand 将 pdf 页面保存为图像

    我想使用 imagemagick Wand 包将 pdf 文件的所有页面转换为单个图像文件 不过 我遇到了以下问题 请参阅下面突出显示问题的评论 import tempfile from wand image import Image wi
  • 当此函数从 scipy.misc 导入时,如何修复“无法导入名称 'imresize' 错误?”

    我正在使用 google colab 来运行 python 代码并尝试缩小图像 from keras layers import Lambda import tensorflow as tf from skimage import data
  • 如何让超级视图拦截按钮触摸事件?

    假设我有这段代码 import
  • 为什么我的 Rails 资源管道中的 js 文件没有被编译?

    我遇到了与这个问题类似的问题 Rails 资产管道不包括 application js 清单中所需的文件 https stackoverflow com questions 15328569 rails asset pipeline not
  • 设置吐司显示长度

    无论如何 我是否可以告诉 Toast 通知仅在指定的时间内显示 通常比常规的 Toast 消息要短 我通过在比标准 toast 持续时间短的一定延迟后调用 toast cancel 找到了解决方案 final Toast toast Toa
  • 如何使溢出 CSS 属性与隐藏值一起使用

    我正经历着一段艰难的时光overflow hidden 基本上 我试图隐藏位于 div 我不知道为什么这不起作用 它没有隐藏它 而是将我的列表从水平布局打破为垂直布局 无序列表是轮播 容器是列表 下面是我的 CSS 代码 div body
  • 可见性会影响 DOM 操作性能吗?

    IE7 Windows XP 我的页面中有一个第三方组件 它会在每次调整浏览器窗口大小时进行大量 DOM 操作来调整自身 不幸的是 我几乎无法控制它的内部功能 并且我已经尽可能地优化了其他所有内容 例如回调和事件处理程序 我无法通过设置 d
  • Swift - 将数组中的值转换为双精度或浮点数

    我有一个数组 其值是字符串 但所有字符串都是 1 0 2 0 等值 我正在尝试将这些字符串转换为双精度数或浮点数 以便我可以将它们全部加在一起 我如何快速做到这一点 let x 1 0 1 5 2 0 print x map Double
  • react-select:如何将 optionRenderer 属性与异步组件一起使用?

    我正在使用反应选择来创建一个选择框 对地址进行地理编码 然后提供该搜索返回的相应地方政府区域的下拉列表 我只是想格式化每个选项 以便它显示状态 例如 昆士兰州在当地政府区域之后 例如 布里斯班 所以我试图让它返回类似的内容 Brisbane
  • Sagemaker 的 IAM 角色?

    我正在尝试让 AWS SageMaker 调用 AWS Comprehend 我在 SageMaker 中收到此消息 ClientError 调用时发生错误 AccessDeniedException StartTopicsDetectio
  • 使用 Ruby 连接到 Oracle 数据库

    我一直无法连接到 Oracle DB 已经阅读了很多内容 但对结果没有任何帮助 我有远程 Oracle DB 我使用 DBVisualizer 设置连接来连接到它 如下所示 DB Type Oracle Driver jdbc Oracle
  • 手机启动时是否可以启动Android应用程序活动?

    我正在尝试构建一个Android应用程序 该应用程序的关键功能之一是它能够在手机启动时自动启动一个活动 我看到我手机上的一些应用程序已经做到了这一点 任何帮助都会很棒 所以我至少可以通过 sdk 对此进行更好的研究 谢谢 您需要像这样实现
  • 使用 tweepy 访问 Twitter 的 Streaming API

    我目前无法获取使用 tweepy 访问 Twitter 的 Streaming API 的示例代码以正确运行 呃 或者至少是我期望它如何运行 我正在使用 GitHub 上最近克隆的 tweepy 标记为版本 1 9 和 Python 2 7
  • 带索引的 Ruby `each_with_object`

    我想要做a each with object with index 以比这更好的方式 a w a b c a each with index each with object arr hash v i arr puts i is i v i
  • 为什么jQuery的.data()函数更能防止内存泄漏?

    关于 jQuery 实用函数 jQuery data 在线文档说 jQuery data 方法允许我们 将任何类型的数据附加到 DOM 元素以安全的方式 循环引用 因此来自 内存泄漏 为什么使用 document body foo 52 可
  • 如何捕获svn合并信息

    我已经检查了主干并完成了从分支到主干的合并 现在 分支中所做的所有更改都存在于我的主干工作副本中 如果我对此工作副本进行提交 有什么方法可以稍后识别它是合并而不是像正常的签出 修改 签入 我可以在日志中指定合并详细信息 但我感兴趣的是 SV
  • 带有一般错误的 Typescript 缩小类型

    有人可以帮我理解这里发生了什么吗 TS游乐场 https www typescriptlang org play code C4TwDgpgBACghgJzgWwDwHkzAJYHsB2AzgHxQC8UA3gLABQUUA2gNJTb5Q