推断时泛型类型丢失

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

在向交集类型添加属性时,我遇到一个奇怪的问题,通用参数丢失。考虑到以下几点:

type Service = Record<string, any>;
type Dependencies = Record<string, Service>;
type Parameters = Record<string, any>;
type HandlerFunction<D, P, R> = (d: D, p: P) => R;

type ServiceInitializer<D extends Dependencies, S = any> = {
  type: string; // ----<HERE>----
} & ((dependencies?: D) => Promise<S>);

function handler<D extends Dependencies, P extends Parameters, R>(
  handlerFunction: HandlerFunction<D, P, R>,
  name?: string,
  dependencies?: string[],
): ServiceInitializer<D, Promise<(p: P) => R>>;

const x = handler(<T>(d: any, { a }: { a: T }): T => a);

如果我删除标有

----<HERE>----
标志的行,我最终会得到以下
x
const 类型,它保留了泛型类型:

const x: <T>(dependencies?: any) => Promise<Promise<(p: {
    a: T;
}) => T>>

但是如果我离开这条线,我就会松开它,

T
unkown
取代:

const x: ServiceInitializer<Record<string, Record<string, any>>, Promise<(p: Record<string, any>) => unknown>>

有没有办法通过仍然允许混合函数和对象属性来保留泛型类型?

typescript typescript-typings
1个回答
2
投票

这只是 TypeScript 3.4 中引入的对“泛型函数的高阶类型推断”支持的限制,由 microsoft/TypeScript#30215 实现。 在 TypeScript 3.4 之前,编译器在通过指定类型参数及其约束来推断其参数或返回类型时,总是会丢失泛型函数类型。 因此,类型为 <T>(x: T)=>T 的函数将“折叠”为 (x: unknown)=>unknown,从而产生

[unknown]
的参数元组和
unknown
的返回类型。
TypeScript 3.4 添加了支持,因此“有时”可以保留泛型类型参数,但这仅发生在非常特殊的情况下:

当函数调用中的参数表达式属于泛型函数类型时,该函数类型的类型参数将传播到调用的结果类型,如果:

被调用的函数是一个泛型函数,它返回带有

单调用签名

的函数类型

单个调用签名本身并不引入类型参数,并且
  • 在函数调用参数的从左到右处理中,没有对参数表达式的上下文类型中引用的任何类型参数进行推断。
  • 就您而言,您显然与第一个(粗体)要点发生了冲突。 仅当被调用函数 (
  • handler()
  • ) 具有返回类型(其本身是具有“单一调用签名”的函数类型)时,才会保留类型参数。
如果您检查代码(
此文件

的第 20041 行附近,该文件太大而无法直接链接到 GitHub),您会发现所谓的“单一调用签名”具有相当的限制性:

// If type has a single call signature and no other members, return that signature.
// Otherwise, return undefined.
function getSingleCallSignature(type: Type): Signature | undefined { ... }

看到了吗? “没有其他成员”。由于 handler() 的返回类型是

ServiceInitializer<D, Promise<(p: P) => R>>
,因此在未添加 
type

成员的版本中,它仅算作“单个调用签名”。 一旦你添加了它,它就不再是“单一调用签名”,并且你会得到 3.4 之前的行为。

为了让不使用您的特定代码的人清楚地了解,最小示例如下所示:
declare function f<A extends any[], R>(f: (...a: A) => R): ((...a: A) => R);
const g = f(<T>(x: T) => x) // const g: <T>(x: T) => T

declare function h<A extends any[], R>(f: (...a: A) => R): ((...a: A) => R) & { x: 0 };
const i = h(<T>(x: T) => x) // const i: ((x: any) => any) & { x: 0; }
函数

f()

返回一个“单一调用签名”,因此传递给它的任何通用函数在返回时都保持通用。但是函数

h()
 返回一个带有额外成员的函数,因此传递给它的任何泛型函数在返回时都将变为非泛型函数。

对高阶类型推理的支持相当脆弱,因此目前这是不可能的。

Playground 代码链接

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