typescript - 输入递归映射类型?

标签 typescript

(不确定该标题)

我想接受任意嵌套的对象和数组,并将所有叶子位置中的基元分成两部分,目标是处理任何类型的“脏”和“原始”值,同时通过两个元素保持统一的可导航性。原始结构和增强结构。

一个简单的例子就像

[1]     -> [{dirty: 1, pristine: 1}]
{x:[1]} -> {x: [{dirty: 1, pristine: 1}]}

类型定义看起来是最简单的部分。我有以下通用条件类型:

class DirtyPristinePrimitive<T> {
    dirty : T; pristine: T;
    constructor(v: T) {this.dirty = v; this.pristine = v;}
}

type DirtyPristine<T> = T extends string
    ? DirtyPristinePrimitive<string>
    : T extends Number
    ? DirtyPristinePrimitive<number>
    : T extends boolean
    ? DirtyPristinePrimitive<boolean>
    : T extends null
    ? DirtyPristinePrimitive<null>
    : T extends undefined
    ? DirtyPristinePrimitive<undefined>
    : T extends Array<infer U>
    ? Array<DirtyPristine<U>>
    : {[k in keyof T]: DirtyPristine<T[k]>};

如果我显式传入类型并在 IDE 中内省(introspection)它们,则上述内容似乎可以工作,无论如何都可以达到几个级别的嵌套。

问题是编写一个函数来从某些 T 创建这种类型的值。我有:

function dirtyPristine<T>(v: T) : DirtyPristine<T> {
    if (Array.isArray(v)) {
        return v.map(e => dirtyPristine(e));
    }
    else if (isObject(v)) {
        const result = {};
        for (const key of Object.keys(v)) {
            (<any>result)[key] = dirtyPristine((<any>v)[key]);
        }
        return result;
    }
    else {
        return new DirtyPristinePrimitive(v);
    }
}

但是在所有 3 个 return 语句中,编译器都会错误提示“Type ... is not allocate to DirtyPristine”。

这可能吗?

最佳答案

问题在于 TypeScript 编译器本质上无法评估 conditional types 的可分配性取决于未指定的 generic输入参数。

当您调用 dirtyPristine()对于特定类型的值,编译器可以推断 {x: string} 的特定类型(如 T )因此评估DirtyPristine<T> (如 {x: DirtyPristinePrimitive<T>} )。没关系。

但是在 dirtyPristine()实现中,类型参数T仍然未指定。编译器在这里基本上放弃了。它推迟 DirtyPristine<T> 的评估,因此不知道如何确定某个值是否实际上可以分配给 DirtyPristine<T> ..除非已知该值的类型为 DirtyPristine<T> .

这是 TypeScript 中的一个痛点,目前尚不清楚应该如何解决。有一个开放的建议,microsoft/TypeScript#33912 ,使用实现内部的控制流来通知编译器返回值可分配给条件返回类型。如果您想看到这种情况发生,您可能需要转到该问题并给它一个👍。但看起来不一定有任何移动,即使这最终发生,你也需要同时继续。

这里的解决方法是仅使用 type assertions如果您确定您的代码生成了预期条件类型的有效实例。或者,因为对每个 return 使用类型断言线很烦人,你可以做道德上的等价并给出 dirtyPristine()单一调用签名overload并使实现签名足够宽松以消除警告(例如 (v: any) => any ):

function dirtyPristine<T>(v: T): DirtyPristine<T>;
function dirtyPristine(v: any): any {
  if (Array.isArray(v)) {
    return v.map(e => dirtyPristine(e));
  }
  else if (isObject(v)) {
    const result: any = {};
    for (const key of Object.keys(v)) {
      result[key] = (dirtyPristine(v) as any)[key];
    }
    return result;
  }
  else {
    return new DirtyPristinePrimitive(v);
  }
}

请注意,这样的断言意味着您已经将验证类型安全性的责任从编译器手中夺走了。如果你犯了错误,它不会警告你,所以要小心。


Playground link

关于typescript - 输入递归映射类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63496898/

相关文章:

javascript - Kotlin 中具有属性的函数类型

node.js - Typescript build "Cannot find module",但仅在 ubuntu 服务器上

node.js - NG Live Development Server 与传统 Web 服务器

mysql - TypeORM 存储库创建不设置值

javascript - 发送 POST 请求 - angular4

node.js - typescript nodejs错误: Cannot find module './package.json'

javascript - 在 Contentful + Angular 应用程序中渲染来自条目的图像

mapbox-gl-draw 的 typescript 定义 d.ts

typescript - 从 Typescript 到 Javascript => required 未在 chrome 浏览器中定义

arrays - 属性在类型上不存在/不能用作索引类型