【问题标题】:Typescript generics, infer object property type without a function call打字稿泛型,无需函数调用即可推断对象属性类型
【发布时间】:2021-07-02 12:34:08
【问题描述】:

这里是一个相当简单的问题。我正在尝试创建一个像这样的泛型类型:

export type RenderObject<TMeta = any> = {
  meta?: TMeta;
  render: (meta: TMeta) => JSX.Element;
}

而且我希望在将对象转换为此类型时,无需将其传递给 RenderObject 泛型参数,或者使用额外的函数包装器或类构造函数来推断 TMeta,即可推断出 TMeta 类型类型。像这样:

const thing: RenderObject = {
  meta: { foo: 'bar' },
  render: (meta) => <i>{meta.foo}</i>,
}

但是,这只是在渲染函数中将TMeta 视为any,而不是从赋值中推断{ foo: string } 类型。

我正在尝试做的事情可能吗?我需要避免额外的函数或类包装器,因为它必须保持高性能并避免向堆栈添加任何额外的调用。抱歉,如果这已经在其他地方得到解答,我似乎无法在任何地方找到这个确切的问题,但也许我的搜索与太多其他类似但不同的问题重叠。

提前致谢!

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    不,这是不可能的。


    microsoft/TypeScript#30120 的问题要求一种用泛型类型注释已声明变量的方法,其中编译器应该推断泛型类型参数而不是需要手动指定。此问题已作为 microsoft/TypeScript#26242 的副本关闭,该提案支持“部分类型参数推断”,这将解决更普遍的问题,即让开发人员在目前唯一的解决方案是手动的情况下要求编译器推断类型参数规格。正如您所注意到的,generic parameter defaults not 解决了这个问题:虽然默认值(如 &lt;T = any&gt;)允许您省略类型参数,但编译器不会推断任何东西当你这样做的时候;它只是将省略的参数替换为默认值。

    如果实现了 microsoft/TypeScript#26242,则可能会编写如下内容:

    // ⚠ NOT VALID TYPESCRIPT, DO NOT USE ⚠
    const thing: RenderObject<infer> = {
        meta: { foo: 'bar' },
        render: (meta) => <i>{meta.foo}</i>,
    };
    

    但现在你不能。 GitHub 问题是开放的,但已经存在了一段时间,最近没有任何明显的动向。我不知道这是否重要,但如果你强烈认为应该支持它,你可能想去那个问题并给它一个?,或者如果你认为它特别重要,请描述你的用例与现有的相比,引人注目。

    如果没有此类支持,您为获得这种行为所做的任何事情都将是一种解决方法。


    我通常提倡的解决方法是使用辅助函数,因为调用泛型函数是编译器实际为您推断类型参数的地方之一:

    const asRenderObject = <T,>(x: RenderObject<T>) => x;
    
    const thing = asRenderObject({
        meta: { foo: 'bar' },
        render: (meta) => <i>{meta.foo}</i>,
    })
    /* const thing: RenderObject<{
        foo: string;
    }> */
    

    这相当优雅,或者至少可以说和RenderObject&lt;infer&gt; 一样优雅。你得到你想要的类型而没有多余的规范。缺点是在运行时,您有一个额外的函数调用。只有您才能真正知道在此处添加调用是否会对代码的运行时性能产生任何明显影响。我对此有点怀疑,因为这意味着您在很短的时间内创建了数千个这样的对象......如果是这样,您可能希望在担心之前考虑重构以提高运行时性能关于 TypeScript 类型推断。从现在开始,我会理所当然地认为额外的函数调用是不可接受的。


    此时我能想到的唯一其他解决方法是手动指定类型;通过注释变量声明:

    interface Thang { foo: string }
    const thing: RenderObject<Thang> = {
        meta: { foo: 'bar' },
        render: (meta) => <i>{meta.foo}</i>,
    };
    

    或通过让编译器推断变量类型,但注释render 回调参数(因为编译器无处可指向type it contextually):

    interface Thang { foo: string }
    const thing3 = {
        meta: { foo: 'bar' },
        render: (meta: Thang) => <i>{meta.foo}</i>,
    };
    

    但是现在根本没有发生类型参数推断......这个“解决方法”是为了完全避免这个问题。


    那么,就这样吧。直到并且除非microsoft/TypeScript#26242 或类似的实现,没有办法做你所要求的。对不起!

    Playground link to code

    【讨论】:

    • 感谢您提供详细信息和解决方法!
    【解决方案2】:

    我试图这样做,但似乎没有办法做到这一点。完全有可能做这样的事情:

    export type RenderObject = {
      meta?: infer U;
      render: (meta: U) => JSX.Element;
    }
    

    但遗憾的是这是不可能的。

    【讨论】:

      猜你喜欢
      • 2021-09-29
      • 1970-01-01
      • 2020-03-22
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-06
      • 2020-03-01
      相关资源
      最近更新 更多