繁体   English   中英

Typescript泛型-将函数参数和返回类型限制为相同类型,并根据使用情况推断类型

[英]Typescript generics - restrict function parameter and return type to same type and infer type from usage

我试图限制函数参数以扩展其中参数和返回值限制为同一类型的函数,而不要求函数本身是通用的。

我有一个简单的工厂函数,该函数创建具有属性的dom节点,如下所示:

function div(className, attributes, children)

EventListener属性(例如onclick)仅限于函数或元组:

type EventHandler<T extends Event> = ((ev: T) => void) | [(ev: T, seed: SameType) => SameType, SameType];

SameType基本上在其中注释您应该提供一个元组,其中第一项是接受种子值的函数,第二个参数指定一旦调用该函数便传递给该函数的初始种子。

我试图使参数本身成为通用参数,将自身传递给类型,然后使用条件匹配,但是没有运气。 我不能在接口本身上使用泛型,因为那没有意义(onclick,oninput和onmouseover都将具有不同的SameType种子值)。

该函数的灵感来自Inferno的linkEvent函数( https://github.com/infernojs/inferno/blob/master/packages/inferno/src/core/types.ts#L45 ),但您只需调用一个函数即可传入一个我认为很方便的元组,但是打字却成了噩梦! 感谢任何输入。

这个问题是否与Typescript不允许在值级别使用泛型有关,还是我缺少某些东西? 我正在考虑这个问题

我会坚持使用每个处理程序上的函数。 确保div函数的on*属性具有一个元组,其中两个元素以您想要的方式关联是可能的,但不是直接的方式。

这将涉及捕获传递给div函数的对象文字的实际类型,并使用条件类型检查此自定义约束:

function div<TAttr extends {
  onClick:EventHandler<MouseEvent>,
  onKeyDown:EventHandler<KeyboardEvent>
}>(attributes: TAttr & CheckAttr<TAttr>): TAttr {
  return attributes;
}

type UnionToIntersection<U> = 
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

type CheckAttr<T> = UnionToIntersection<CheckAttrHelper<T>[keyof T]> 
type CheckAttrHelper<T> = {
  [K in keyof T]:
    T[K] extends [(ev: Event, seed: infer P) => infer R, infer S] ?
      ([R] extends [P] ? [S] extends [P] ? never
          : Record<K, "Seed type is not assignable to parameter type">
          : Record<K, "Return type is not assignable to parameter type">
      ): never
}

type EventHandler<T extends Event> = ((ev: T) => void) | [(ev: T, seed: any) => any, any];

//ok
let a = div({
  onClick: [(ev, n: string | number) => 2, "1"],
  onKeyDown: [(ev, n: number) => n + 1, 1]
});

//err 
// Type '(string | ((ev: MouseEvent, n: string) => number))[]' is not assignable to type 
//'[(ev: MouseEvent, n: string) => number, string] & 
// "Return type is not assignable to parameter type"'.
let a2 = div({
  onClick: [(ev, n: string) => 2, "1"],
  onKeyDown: [(ev, n: number) => n + 1, 1]
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM