【问题标题】:Does TypeScript allow a type alias to specify generics?TypeScript 是否允许类型别名来指定泛型?
【发布时间】:2017-09-18 13:50:31
【问题描述】:

我希望能够为一个非常通用的函数起别名并指定通用参数的部分,从而创建同一函数的通用性较低的版本。类似于以下内容:

function veryGeneric<X, Y>(someParam: Y): { result: X } {
  // ...
}

type LessGeneric = typeof veryGeneric<X, string>
const lessGeneric: LessGeneric = veryGeneric

我希望lessGeneric 函数基本上被键入为:

function lessGeneric<X>(someParam: string): { result: X } {
  // ...
}

这有可能吗?

我知道我可以创建一个包装函数,但我宁愿不必再次指定参数类型(并且不必支付另一个函数调用的开销,即使它很小,也将是一个奖励)。


这是我正在处理的真实示例。给定一个函数声明(来自react-tracking),如下所示:

declare function track<T = {}, P = {}>(trackingInfo?: TrackingInfo<T, P>, options?: Options<Partial<T>>): Decorator

我希望能够定义一个别名来指定 trackingInfo 参数的类型,但保留 P 通用。即,我想要一个基本上键入为的别名:

interface ValidAnalyticsEntries {
  page: string
  action: string
}

declare function trackSpecificToOurAnalyticsSchema<P = {}>(trackingInfo?: TrackingInfo<ValidAnalyticsEntries, P>, options?: Options<Partial<ValidAnalyticsEntries>>): Decorator

【问题讨论】:

  • 这里有什么意义?没有使用 X 泛型约束,为什么会在那里?请分享你的真实代码
  • 我不认为有必要知道 X 对这个示例做了什么,不过我添加了一个简单的说明。
  • 如果你能更好地描述你的问题,你可以获得更好的帮助,而在这种情况下,它没有得到很好的解释。如果您只在函数中使用它并且它不是参数或返回类型,则无需将X 作为通用约束。
  • 我明白这一点,但给人的印象是// ... 部分清楚地表明存在与手头问题无关的细节。我现在已经添加了真实的例子。
  • 您会使用相同的功能(track)还是实际实现trackSpecificToOurAnalyticsSchema?

标签: function typescript generics type-alias


【解决方案1】:

要定义泛型类型别名,您可以定义一个描述函数签名的接口:

interface VeryGeneric<X, Y> {
    (someParam: Y): { result: X };
}

type Foo = { foo: number };
type LessGeneric<X = Foo> = VeryGeneric<X, string>;

const lessGeneric: LessGeneric = veryGeneric;

【讨论】:

  • 谢谢!我仍然需要使用包装函数来让不太通用版本的用户指定X,但至少消费者可以重用接口并生成具有自定义默认值的函数。你可以在这里找到这些例子github.com/artsy/reaction/blob/…
【解决方案2】:

你可以这样做:

const lessGeneric: <X>(someParam: string) => { result: X } = veryGeneric;

我只是拼写出所需的 lessGeneric 类型,而不是试图强制 TypeScript 首先将 veryGeneric 的类型转换为 lessGeneric 的类型。但是没有包装函数。

这对你有用吗?如果没有,请在您的用例和示例中添加更多详细信息。具体来说,X 参数几乎不可能实现(实现如何获得X 类型的值)并且Y 参数几乎什么都不做(someParam 参数的类型为@ 987654329@,但没有其他东西在使用Y,所以您不妨将someParam 声明为any 类型而不使用Y)。

希望有所帮助;祝你好运!


编辑:我对真实示例的建议如下:

const trackSpecificToOurAnalyticsSchema: <P = {}>(
  trackingInfo?: TrackingInfo<ValidAnalyticsEntries, P>,
  options?: Options<Partial<ValidAnalyticsEntries>>)
  => Decorator = track;

或者,从ValidAnalyticsEntries抽象出来:

type PartiallySpecifiedTrack<T = {}> = <P = {}>(
  trackingInfo?: TrackingInfo<T, P>,
  options?: Options<Partial<T>>)
  => Decorator

const trackSpecificToOurAnalyticsSchema: 
  PartiallySpecifiedTrack<ValidAnalyticsEntries> = track;

请注意,在所有这些情况下,您仍然必须至少写出函数签名两次:一次是定义完全通用的track(),一次是定义PartiallySpecifiedTrack。但是,如果需要,您可以将 PartiallySpecifiedTrack 与 T 的不同值重复使用:

const anotherTrack: PartiallySpecifiedTrack<{ foo: string }> = track;
declare const trackingInfo: TrackingInfo<{ foo: string }, {bar: number}>
anotherTrack(trackingInfo); // okay

好的,这是我能做的最好的了。祝你好运!

【讨论】:

  • 感谢您的建议!确实没有包装函数,所以这绝对是一个进步,但我最重要的原因是我不想再次拼出所有参数。
  • 谢谢。我还没有找到比我上面的答案更简洁的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-03
  • 2014-08-19
  • 1970-01-01
相关资源
最近更新 更多