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

时间:2021-04-06 17:23:52

标签: typescript typescript-generics

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

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

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

TMeta

然而,这只是在渲染函数中将 const thing: RenderObject = { meta: { foo: 'bar' }, render: (meta) => <i>{meta.foo}</i>, } 视为 TMeta,而不是从赋值中推断 any 类型。

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

提前致谢!

2 个答案:

答案 0 :(得分:3)

不,这是不可能的。


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

如果 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<infer> 一样优雅。您可以在没有冗余规范的情况下获得所需的类型。缺点是在运行时,您有一个额外的函数调用。只有您才能真正知道添加调用是否会对代码的运行时性能产生明显影响。我对此有点怀疑,因为这意味着您在很短的时间内创建了成千上万个这样的对象……如果是这样,您可能希望在担心之前研究重构以提高运行时性能关于 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

答案 1 :(得分:0)

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

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

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