你能在 TypeScript 中指定对象字面量的类型吗?

2024-03-30

我想知道是否有一种方法可以指定对象文字的类型。

例如,如何解析此代码并分配一个B字面意思是A多变的:

interface A {
    a: string;
}

interface B extends A {
    b: string
}

const a: A = {
    a: "",
    b: ""
};

B is an A,所以我希望能够分配一个B其中一个A是期待。但是,编译器没有足够的上下文来确定我正在传递一个B而不是非法的A,所以我需要指定它。

我不想只是让上面的代码编译,我特别想要我的意图“我正在分配一个B to an A”被传达给编译器。我想要具有类型安全性B对象字面量,所以如果B例如,获取其他属性,这应该无法再次编译。

我偶然发现这个帖子 https://stackoverflow.com/questions/19456105/how-can-i-specify-a-typed-object-literal-in-typescript当搜索这个时。一种选择是定义一个恒等函数:

const is = <T>(x: T): T => x;

const a: A = is<B>({
    a: "",
    b: ""
});

这并不理想,因为它会为了转译后不相关的东西而运行代码。另一种选择是使用第二个变量,如下所示:

const b: B = {
    a: "",
    b: ""
};

const a: A = b;

出于同样的原因,它也不理想,它是用于 TypeScript 检查的代码。

@jcalz 的建议:

interface A {
  a: string;
  [k: string]: unknown; // any other property is fine
}

当我想要时,这允许任何多余的属性通过A及其要传递的子类型。A的封闭性不是问题,我只是无法告诉编译器我正在传递一个B其中一个A是期待。

Turn off --suppressExcessPropertyErrors compiler option

我想抑制多余的属性,问题是我无法告诉编译器这些不是多余的属性,它们是正确子类型的属性。

是否有与文字类型声明等效的东西? TypeScript 是否存在阻止此类构造存在的基本设计?

在 Kotlin 这样的语言中,这是微不足道的:

val a: A = B()

您可以分别声明变量的类型和运行时对象的类型(必须是相同类型或子类型)。

如果这是一个XY问题 https://en.wikipedia.org/wiki/XY_problem,我最初试图解决的问题如下所示:

const foo = (): A => {
    if (/* condition */) {
        /* some setup.. */
        return {
            a: "",
            b: ""
        }
    } else {
        return {
            a: ""
        }
    }
}

这不会编译,因为第一个return不是明确的A,它的目的是成为B which is an A,但我无法将其指定为B而不诉诸我上面提到的解决方法之一。我想要看起来像的东西

return {
    a: "",
    b: ""
}: B

// or

return: B {
    a: "",
    b: ""
}

// or

return {
    a: "",
    b: ""
} is B

不用说都会那样进行as B类型断言不是我在这里寻找的。我不想绕过类型系统,我只是想帮助编译器检查我的对象文字是否为指定类型。这个问题也适用于参数:

const foo = (a: A) => {};

foo({ a: "", b: "" })

这失败了,因为我无法告诉编译器我正在传递一个B,它认为我正在使用多余的属性。只有当我使用额外的变量时它才有效,如下所示:

const b: B = { a: "", b: "" };

foo(b)

TS4.9+ 更新

现在有一个satisfies操作员 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html#the-satisfies-operator它允许您根据类型检查值,而无需将值扩展到该类型。因此,这个答案的新版本看起来像:

interface A {
    a: string;
}
interface APlus extends A {
    [k: string]: unknown;
}

const bad = { a: 123 } satisfies APlus; // error
// ---------> ~ Type 'number' is not assignable to type 'string'.
// const bad: { a: number; }

const good = { a: "", b: "" } satisfies APlus; // okay
// const good: { a: string; b: string;}

Here bad是错误的,因为它的a属性类型错误。和good火柴APlus(这是A明确允许多余的属性)但其类型仍然是{a: string, b: string}.

Playground 代码链接 https://www.typescriptlang.org/play?target=99#code/JYOwLgpgTgZghgYwgAgILIN4Chm+XALmQGcwpQBzAbiwF8tRJZEVUAFAGwFdjkIAPSCAAmvdNjzIA2gGsipciAoBdIlxAyQAewDuIGvSwItIUsgBGcYcgC8mfEQCMAJgDMyWiThhgxGMAgxTh4qZAB6ML4oKC0oLAjkAFpklJSAPmQAP2QAFQBPAAcUAHIQLgBbc2hi5F9kbTB8YmJgChA4cw4UMC1kMEKShUpigDp4yONTRsthIgwHeoqqqFDDSbMKLS1rO3nCZAAiA4AaCyIjjy8fPwCg7mJQhK0ZODzx5HXGze25haGlULmeRkShUWhAA


TS4.8 的答案-

更新:这里的主要问题似乎是 TypeScript 没有“仅加宽断言运算符”或“内联类型注释运算符”(例如,return {a: 123} op A应该失败但是return {a: "", b: ""} op A应该会成功)。有一个开放建议之一,请参阅 microsoft/TypeScript#7481 https://github.com/microsoft/TypeScript/issues/7481但那里没有真正的动静。如果您想看到这种情况发生,您可能需要转到该问题并给它一个????和/或描述您的用例(如果它比现有的更引人注目)。如果没有明确的类型运算符,可以使用一些策略和解决方法。


听起来您已经知道处理此问题的最常见选项,但您不喜欢其中任何一个。我还有一些其他的事情你可以尝试,但如果你的限制是:

  • 无需更改类型定义或注释
  • 发出的运行时代码没有变化
  • no 类型断言 https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions

那么你的选择确实有限,你可能找不到任何令人满意的答案。


发生事件的背景:

对象字面量是 TypeScript 正常可扩展对象类型的一个有趣的例外。一般来说,TypeScript 中的对象类型不是exact https://github.com/microsoft/TypeScript/issues/12936。类型定义如{a: string}仅要求一个值必须具有一组已知属性。它不要求值必须缺乏未知属性。所以一个对象{a: "", b: 123, c: true}是一个完全有效的类型值{a: string},因为它有一个a其值可分配给的属性string。所以看起来你的代码应该没有什么;你的a确实是有效的A,有或没有上下文。

但编译器当然会抱怨。这是因为您已将新的对象字面量分配给一个用已知属性少于对象字面量的类型进行注释的变量。因此它发生了冲突超额财产检查 https://www.typescriptlang.org/docs/handbook/interfaces.html#excess-property-checks,其中对象类型被视为精确的。你可以阅读实现此功能的拉取请求 https://github.com/microsoft/TypeScript/pull/3823这样做的理由 https://github.com/microsoft/TypeScript/issues/3755。这些问题还详细介绍了针对不需要进行过多属性检查的情况的建议解决方法。


您已经提到过创建一个辅助函数(例如,return is<A>({...})),分配给中间变量(例如,const ret = {...}; return ret;),并使用类型断言(例如,return {...} as A)。以下是我看到的其他选项:

  • 添加您的索引签名A键入以使其显式打开:

      interface A {
        a: string;
        [k: string]: unknown; // any other property is fine
      }
    
      const a: A = {  a: "", b: "" }; // okay now
    
  • 完全关闭多余的属性检查the --suppressExcessPropertyErrors编译器选项 http://www.typescriptlang.org/docs/handbook/compiler-options.html#compiler-options(我真的不推荐这样做,因为这是一个范围广泛的变化)

  • Use a 联合型 http://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types like A | B代替A在注释对象字面量对应的类型时:

      const a: A | B = { a: "", b: ""  }; // okay now
    

    Since B是一个子类型A, 方式A | B结构上等价于A,所以你可以用它代替A几乎任何地方:

      const foo = (): A | B => { ... } // your impl here
      function onlyAcceptA(a: A) { }
      onlyAcceptA(foo()); // okay
    
  • 仅在额外的属性上使用类型断言,这需要使用计算属性名称 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names:

       const a: A = { a: "", ["b" as string]: "" }; // okay
    

    这实际上并没有降低类型安全性,但它最终会生成略有不同的 JavaScript ({a: "", ["b"]: ""}),假设您的目标是支持计算属性名称的 JavaScript 版本。

我能想到的其他一切只会更多地改变你的运行时代码,所以我就到此为止。


不管怎样,希望能给你一些想法。祝你好运!

链接到代码 https://www.typescriptlang.org/play/#code/HYQwtgpgzgDiDGEAEB5GFgBUCe6kG8BYAKCSQEtgAXCAJwDMFkBBAksskALiSitsoBzANzsOAbQDWPPgOCCAujwCuwScAD2Ad2DCkAen1IQwbEg1UAFnSQxaG9LSpnyUJPUoQxAXxJjKNAxMSABCSBAAHjTAACZurESkHABGMvxCPn5J8BrAfMY8rAC8bEmcPABEFQA0YmSpSFU+eobmkiBmmlpZZDl5VO4aGkglABQAlIUjAHylHBT0SKMAsiBWAHS0JjEaYBNIADxIAAzrAKzjc-NItBBUyrTAV9cFjTV11w1NZRy+P97hAA2UGQiRet3uj2e124bw+ZD+80RSD+fxIoEgsGCAFVgORclcAnRGIgkAkPrDZEJREk0UkiUFSWFItE4mToQ0qfJMsQxH18rDWAAfUIjaGwqq1H5fCrNAxGDTtTraHpIfkDehDMUTKYisJFWZgjjkRYrNaWTbbXb7I6nC7QsgQh5PI0vCXvH7zGXwlEfAEQYGgn1OqGumGVWWe33-HlieiqeBUfFPXKA7DMeCIGBUZijQWXfDRsip9OZiDZ3OajQTcYtBVKrJ0jHQOCk5hQEFOACiUS2AAV7DB2wBldLyUr+ajE4Lkn6UsciWP0qeM5DMqIYNmzr1pOSCJe9XICqYlMPuqXzcQVZIVYxuLmKCNy1qKjpILokOkkIA

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

你能在 TypeScript 中指定对象字面量的类型吗? 的相关文章

随机推荐

  • 如何测试 GAS 中的触发功能?

    Google Apps 脚本支持Triggers https developers google com apps script understanding triggers 那次通过Events https developers goog
  • 是否有 pycharm_helpers 的存储库

    最近我开始使用皮查姆 2017用于Docker 中的远程调试我意识到 PyCharm 的每个版本都会有一个新的图像pycharm helpers被拉取 根据构建使用不同的标签 有什么办法可以下载全部吗pycharm helpers图片 或者
  • 在 tibble 中添加具有可变列名称的列

    此代码无法在 tibble 中添加列 library tidyverse df lt data frame Oranges 5 mycols lt c Apples Bananas Oranges add column df mycols
  • spring mvc 不返回 json 内容 - 错误 406

    我正在使用 Spring MVC 和 JSON 如中指定的Ajax 简化 Spring 3 0 文章 http blog springsource org 2010 01 25 ajax simplifications in spring
  • IIS 10 上的 OWIN WebApi 项目对所有操作均给出 404

    我有一个 OWIN WebAPI 项目 它在 OwinSelfHost 中工作 但在 IIS 10 上托管时总是导致 404 唯一有效的是初始页面 public index html 我在 Windows 10 计算机上运行 IIS 10
  • 如何在meteor上创建默认用户?

    如果不存在用户 我想创建一个管理员用户 我在服务器文件夹内的 js 文件上尝试过 Meteor startup function if Meteor users find count var options username admin p
  • 如何用反应钩子停止负数?

    我使用 React Hook 来增加和减少数字 但是当减少到0以下然后计算负值时我遇到了一个问题 我不想要负值 如何使用react hook停止负值 我已经尝试过这段代码 import React useState useEffect fr
  • 设置 DateTimePicker 的绑定值

    我有一个名为 Employee 的 EF 实体 它有一个可为空的 DateTime 属性 TerminationDate DisplayName Termination Date DisplayFormat ApplyFormatInEdi
  • 访问 VBA OpenForm 分组和排序

    我有一个用于数据输入的表格 我们必须返回并添加数据到这些记录中 有没有办法拉出按字段 A 对记录进行分组并按字段 B 排序的表单 这本质上会对表格 A1 1 A1 2 等进行排序 从而使添加数据变得更加容易 现在我使用 DoCmd Open
  • 服务器端处理 JWT 令牌的最佳实践[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 产生自这个线程 https stackoverflow com questions 30494383 using jwt with active
  • WPF 列表框 IndexFromPoint

    我正在 WPF ListBoxes 之间执行拖放操作 我希望能够将其插入到集合中被拖放的位置而不是列表末尾 有谁知道类似于 WinForms ListBox IndexFromPoint 函数的解决方案 我最终通过使用 DragDropEv
  • 处理 Reactor 中的并联通量

    我已经从 iterable 创建了一个并行通量 对于每个可迭代 我都必须进行休息调用 但是在执行时 即使任何一个请求失败 所有剩余的请求也会失败 我希望所有的请求都能被执行 无论失败或成功 我目前正在使用 Flux fromIterable
  • Html.Partial 不渲染部分视图

    我在视图中有以下代码 if SiteSession SubPageHelper DisplayType DisplayType List Html Partial SubLandingPage List else Html Partial
  • 可达性未按预期工作

    从 Apple 下载了 Reachability 使用此方法检查活动连接 BOOL isReachable Reachability r Reachability reachabilityWithHostName http www goog
  • LinkedIn 身份验证和聚合数据

    我正在使用 Ruby on Rails 构建一个 Web 应用程序 我希望我的用户能够验证和聚合来自 Linked In 以及其他类似 Github Twitter 等 的数据 我正在使用这些宝石 设计注册 用于身份验证的omniauth
  • Android:更改按钮的父视图

    我有一个RelativeLayout 里面有一个按钮 一旦用户单击该按钮 我想更改父视图 RelativeLayout 的背景 我知道我可以通过将父视图存储在变量中或在按钮上设置标签来做到这一点 但我会避免这种情况 我有充分的理由不希望这样
  • 动画 SVG 虚线

    我想制作一个动画虚线在 SVG 文件中 该线应该从 0 长度 增长 到全长 我发现的所有方法都不适合我 有谁有想法 如何解决这个问题 这是我的 svg 文件中的路径
  • ASP.NET MVC Razor 视图中的 Html.Raw()

    int count 0 foreach var item in Model Resources count lt 3 Html Raw div class ToString Html Raw some code count lt 3 Htm
  • 如何恢复 XGBoost 特征重要性图中的原始特征名称(预处理删除它们后)?

    在训练 XGBoost 模型之前对训练数据进行预处理 例如居中或缩放 可能会导致特征名称丢失 SO 上的大多数答案建议以不会丢失特征名称的方式训练模型 例如在数据框列上使用 pd get dummies 我使用预处理数据训练了 XGBoos
  • 你能在 TypeScript 中指定对象字面量的类型吗?

    我想知道是否有一种方法可以指定对象文字的类型 例如 如何解析此代码并分配一个B字面意思是A多变的 interface A a string interface B extends A b string const a A a b B is