Typescript 泛型,无需函数调用即可推断对象属性类型

问题描述 投票:0回答:2

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

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
2个回答
11
投票

不,正如所述,这是不可能的。


microsoft/TypeScript#32794microsoft/TypeScript#30120 中的问题要求一种用泛型类型注释声明的变量的方法,其中编译器应推断泛型类型参数而不是需要手动指定。 后一个问题作为 microsoft/TypeScript#26242 的重复项而关闭,这是一项支持“部分类型参数推断”的提案,该提案将处理更普遍的问题,即让开发人员要求编译器在当前情况下推断类型参数唯一的解决方案是手动指定。 正如您所注意到的,通用参数默认值解决了这个问题:虽然默认值(如

<T = any>
)允许您省略类型参数,但编译器不会在您这样做时推断任何内容;它只是用默认值替换省略的参数。

如果 microsoft/TypeScript#26242 或 microsoft/TypeScript#32794 得到实现,可能会编写如下内容:

// ⚠ 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<infer>
一样优雅。 您无需多余的规范即可获得所需的类型。 缺点是在运行时,您需要进行额外的函数调用。 只有您才能真正知道添加调用是否会对代码的运行时性能产生明显影响。 我对此有点怀疑,因为这意味着您在很短的时间内创建了数千个这样的对象......如果是这样,您可能需要研究重构,以便在担心之前提高运行时性能关于 TypeScript 类型推断。 从现在开始,我将理所当然地认为额外的函数调用是不可接受的。


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

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

或者让编译器推断变量类型,但注释

render
回调参数(因为编译器无法根据上下文键入它):

interface Thang { foo: string } const thing3 = { meta: { foo: 'bar' }, render: (meta: Thang) => <i>{meta.foo}</i>, };
但是现在根本没有发生类型参数推断......这个“解决方法”是完全避免这个问题。


那么,就这样吧。 除非实现

microsoft/TypeScript#26242 或类似的内容,否则无法执行您所要求的操作。 对不起!

Playground 代码链接


1
投票
我尝试这样做,但似乎没有办法做到。完全有可能做这样的事情:

export type RenderObject = { meta?: infer U; render: (meta: U) => JSX.Element; }
但遗憾的是这是不可能的。

© www.soinside.com 2019 - 2024. All rights reserved.