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

Wil*_* P. 8 typescript typescript-generics

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

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

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

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

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

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

提前致谢!

jca*_*alz 10

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

\n
\n

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

\n

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

\n
// \xe2\x9a\xa0 NOT VALID TYPESCRIPT, DO NOT USE \xe2\x9a\xa0\nconst thing: RenderObject<infer> = {\n    meta: { foo: \'bar\' },\n    render: (meta) => <i>{meta.foo}</i>,\n};\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
\n

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

\n
const asRenderObject = <T,>(x: RenderObject<T>) => x;\n\nconst thing = asRenderObject({\n    meta: { foo: \'bar\' },\n    render: (meta) => <i>{meta.foo}</i>,\n})\n/* const thing: RenderObject<{\n    foo: string;\n}> */\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
\n

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

\n
interface Thang { foo: string }\nconst thing: RenderObject<Thang> = {\n    meta: { foo: \'bar\' },\n    render: (meta) => <i>{meta.foo}</i>,\n};\n
Run Code Online (Sandbox Code Playgroud)\n

或者让编译器推断变量类型,但注释render回调参数(因为编译器无法根据上下文键入它):

\n
interface Thang { foo: string }\nconst thing3 = {\n    meta: { foo: \'bar\' },\n    render: (meta: Thang) => <i>{meta.foo}</i>,\n};\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
\n

那么,就这样吧。除非实现microsoft/TypeScript#26242等,否则无法执行您所要求的操作。对不起!

\n

Playground 代码链接

\n

  • 感谢您提供详细信息和解决方法! (2认同)