确保泛型类型在 Typescript 中仅具有原始属性

2024-01-18

我有一个采用泛型类型的函数,我需要确保该类型是 JSON 可序列化的(也称为仅原始属性)。

我的尝试是为 JSON 兼容类型定义一个接口,并强制我的泛型扩展此类型:

type JSONPrimitive = string | number | boolean | null
interface JSONObject {
  [prop: string]: JSONPrimitive | JSONPrimitive[] | JSONObject | JSONObject[]
}
export type JSONable = JSONObject | JSONPrimitive | JSONObject[] | JSONPrimitive[]

function myFunc<T extends JSONable>(thing: T): T {
  ...
}

// Elsewhere

// I know that if this was defined as a `type` rather than
// an `interface` this would all work, but i need a method
// that works with arbitrary types, including external interfaces which
// are out of my control
interface SomeType {
  id: string,
  name: string
}

myFunc<SomeType[]>(arrayOfSomeTypes)
// The above line doesn't work, i get: 
// Type 'SomeType[]' does not satisfy the constraint 'JSONable'.
//   Type 'SomeType[]' is not assignable to type 'JSONObject[]'.
//     Type 'SomeType' is not assignable to type 'JSONObject'.
//       Index signature is missing in type 'SomeType'.ts(2344)

这里的问题似乎归结为索引签名在打字稿中的工作方式。具体来说,如果类型缩小了索引签名允许的可能属性,则该类型无法使用索引签名扩展类型。 (IESomeType不允许你随意添加foo财产,但是JSONable当然会。这个问题在这篇文章中有进一步的描述现有的 github 问题 https://github.com/Microsoft/TypeScript/issues/1897.

所以我知道上面的方法并没有真正起作用,但问题仍然存在,我需要一些可靠的方法来确保泛型类型是 JSON 可序列化的。有任何想法吗?

提前致谢!


我可能会在这里进行的方式(在没有修复或更改的情况下)接口中隐式索引签名的潜在问题 https://github.com/microsoft/TypeScript/issues/15300) 会将您想要的 json 类型表示为类似于这样的通用约束:

type AsJson<T> = 
  T extends string | number | boolean | null ? T : 
  T extends Function ? never : 
  T extends object ? { [K in keyof T]: AsJson<T[K]> } : 
  never;

So AsJson<T>应该等于T if T是一个有效的 JSON 类型,否则它将有never在它的定义中的某个地方。然后我们可以这样做:

declare function myFunc<T>(thing: T & AsJson<T>): T;

这要求thing be T(这推断T为你)相交的 with AsJson<T>,这增加了AsJson<T>作为一个额外的限制thing。让我们看看它是如何工作的:

myFunc(1); // okay
myFunc(""); // okay
myFunc(true); // okay
myFunc(null); // okay

myFunc(undefined); // error
myFunc(() => 1); // error
myFunc(console.log()); // error

myFunc({}); // okay
myFunc([]); // okay
myFunc([{a: [{b: ""}]}]); // okay

myFunc({ x: { z: 1, y: () => 1, w: "v" } }); // error!
//  --------------> ~
//  () => number is not assignable to never

现在您的接口类型已被接受:

interface SomeType {
  id: string;
  name: string;
}

const arrayOfSomeTypes: SomeType[] = [{ id: "A", name: "B" }];
myFunc(arrayOfSomeTypes); // okay

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

链接到代码 http://www.typescriptlang.org/play/#code/C4TwDgpgBAggzgKTgewHYB4AqA+KBeKAKCikyggA9gJUATOKOYAJwEtUBzKAHylQFcAtgCMIzHlGHJkAGwgBDVBIEyZUAPykoALiIkylanQYAxfqgDGwVmg18IANzE69WwzXpRkwgFYQrdgDeUADaANJQ7FAA1hAgyABmpAC6uvBIaFjhybgAvi7E9k7MANyEhLT+MvLM0AnmVjZKgiBmlljYABTAABbsHLpkAGSwiCgYOACUg2WELW0WnQCMkyVQAPTrXtHyIHOtDZ0AREerG1vIO3vzhyz8EGeb27v7C50qMo8XV+U3lp3mSoJdgQWhfcjMZjIZivQ6dSb4XArNZPMRQmF-RYWNAoOQAOhkyA48PBaOhvwO-0CuXBlxemM6IWStJ+DJCgXkunZwl0J1yyX5LJesKpUAoumCAC9dEsADRQEC6eGIqByqAAd15DiOUHyNJRWzJzAAhIQnlAALRW602m24AB+Zq2UGVeFwAhEzlYDFQyGAUHkcDgrA4qHkwjkUGAyCKYnKhHY1GYCXkFmgAGVkIIIJhwNBAoVWLRdEw2JwyiQw9mSyx+mVcuVsagmAHIbsAPIJTPZ3OQOC6bs5vNM-ChYJF3kwI7yqsQXkAIR1-LKDJqzA7XazQ77Qr2QA

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

确保泛型类型在 Typescript 中仅具有原始属性 的相关文章

随机推荐

  • 使用常量字符串列表中的 INNER JOIN 进行 SQL INSERT

    我想创建一个 SQL Server 2012 查询 它插入一个常量的权限名称列表 例如将 ViewUsersPermission ModifyUsersPermission 添加到表 RolePermissions 中 该表有两列 Role
  • 如何消除日期抖动中的时间

    我想从这个日期中删除时间格式 我想显示这样的日期22 10 2019 or 2019 10 22 2019 10 22 00 00 00 000 现在有一个DateUtils https api flutter dev flutter ma
  • 将 NSTimer 设置为将来触发一次

    如何设置 NSTimer 在将来触发一次 例如 30 秒 到目前为止 我只能将其设置为立即触发 然后每隔一段时间触发 您要使用的方法是 NSTimer scheduledTimerWithTimeInterval NSTimeInterva
  • JSF UrlRewriteFilter 包罗万象/404 替换

    我正在使用 Tuckey UrlRewrite 设置 URL 规则 到目前为止一切正常 但是我正在努力处理我的默认页面 客观的 任何与现有文件不匹配的请求 或者 任何不符合先前规则的请求 应该启动搜索search jsf q 它的目的是处理
  • 在控件的模式弹出扩展器上显示消息框

    我在页面内有一个控件 该控件具有以下模式弹出窗口扩展程序
  • Maven 依赖与多模块?

    对 Maven 非常陌生 有人可以向我解释一下使用 Maven 模块与仅将 Maven 项目的依赖项添加到工作区中的另一个 Maven 项目之间的区别吗 你什么时候会使用其中一种而不是另一种 依赖项是一个预先构建的实体 您可以从 Maven
  • 如何禁用 contenteditable div 中的元素选择和调整大小?

    例如 我有以下布局 div span class text block span Name span a href i class small icon remove i a span div 那么 如何禁用它 和这个 当我尝试完全隐藏控件
  • 列的类型为时间戳,没有时区,但表达式的类型为字符

    我正在尝试在 Redshift 上实施 SCD2 时插入记录 但出现错误 目标表的DDL是 CREATE TABLE ditemp ts scd2 test id INT md5 CHAR 32 record id BIGINT IDENT
  • AutoFixture 3 生成的整数是否唯一?

    生成的整数是IFixture Create
  • Knockout 订阅可观察对象

    我有一个对象 model settings FirstName Joe LastName Bloggs 在我的视图模型中 我将设置设置为可观察的 this Settings ko observable ko mapping fromJS m
  • 如果不存在相同的整行,MySQL INSERT

    我有一个包含 10 列的表 我必须从 CSV 文件添加很多很多行 当然 我不能添加两个相同的行 因此我需要一个 SQL 语句 如果整行确实存在 则忽略该命令 仅当所有字段都相同时才必须忽略 INSERT 两行可能有相同的field1 or
  • ActiveRecord:返回对象时隐藏列

    是否有一种开箱即用的方法可以在返回 ActiveRecord 对象时始终隐藏 删除列 例如 User password 使用内置序列化 您可以覆盖as json模型上的方法来传递其他默认选项 class User lt ActiveReco
  • 如何在 Robolectric 中测试选项菜单项的可见性?

    我想断言菜单项的可见性 但是 我的菜单项总是返回 true 我正在使用以下代码来扩充我的菜单 SherlockMenuInflater inflater new SherlockMenuInflater activity MenuBuild
  • simplecursoradapter textview 给出 nullpointerException

    我有两个 xml 文件 一个是列表视图 另一个是列表视图和一些 texview 的布局 我想更改第二个 xml 文件中文本视图的颜色 这就是我到目前为止所做的 main1 xml
  • Laravel Eloquent `take` 和 `orderBy`

    当我尝试使用每个 take 和 orderBy 查询时 模型返回一些记录 this gt hasMany App User gt take 3 this gt hasMany App User gt orderBy id desc 但是当我
  • 如何在CSS中制作具有透明度的径向渐变

    我想在透明度变化的地方制作一个径向渐变 我可以让它线性工作 但不是径向工作 background webkit gradient linear left top left bottom from rgba 50 50 50 0 8 to r
  • 具有高多边形网格的 OpenGL 3D 光线拾取

    如何在包含高多边形网格的模型的 3D 场景中实现 3D 光线拾取 迭代所有三角形来执行三角形线相交测试需要花费太多时间 我知道存在八叉树等方法 并且应该可以将这些方法用于场景中的模型 但我不知道应该如何在网格级别使用这些概念 但是 如果您在
  • GWT - 如何编译移动排列

    我知道如何使用延迟绑定为不同的用户代理编译 GWT 应用程序 但这似乎没有提供区分桌面 移动浏览器的方法 除了制作基于 gwt mobile webkit 的新应用程序之外 如何将现有的 GWT 应用程序转换为具有重新设计的移动界面 如果您
  • 如何使maven-compiler-plugin不隐藏错误源位置

    也许有一个maven compiler plugin这个选项 但我还没有找到 When javac直接运行并打印错误 在消息的第一行之后 它显示受影响的源行下一行上有一个插入符号指向错误位置 它看起来像这样 com invariantpro
  • 确保泛型类型在 Typescript 中仅具有原始属性

    我有一个采用泛型类型的函数 我需要确保该类型是 JSON 可序列化的 也称为仅原始属性 我的尝试是为 JSON 兼容类型定义一个接口 并强制我的泛型扩展此类型 type JSONPrimitive string number boolean