【问题标题】:Typescript generics - restrict function parameter and return type to same type and infer type from usageTypescript 泛型 - 将函数参数和返回类型限制为相同类型并从使用中推断类型
【发布时间】:2019-05-09 04:38:14
【问题描述】:

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

我有一个简单的工厂函数,它创建具有属性的 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 在值级别上不允许泛型有关,还是我遗漏了什么?我在想this issue

【问题讨论】:

    标签: typescript generics


    【解决方案1】:

    我会坚持每个处理程序上的功能。在 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]
    });
    

    【讨论】:

    • 谢谢!这是一个非常有趣的解决方法,我想我可以让它工作。唯一的缺点是它只进行类型检查,它不会以与模板泛型推断类型相同的方式推断类型(方法 fn(a: T): T 将从 a 或返回值推断 T功能)。即使您返回 5,seed 仍然是 any,直到您将其显式键入 number 或其他触发 CheckAttr 的内容。对于 Javascript 文件的 VSCode linting,实现推断类型更为有用,因为您无需显式使用 JSDoc 符号即可自动完成。
    猜你喜欢
    • 2020-01-23
    • 2016-11-01
    • 1970-01-01
    • 2016-12-05
    • 2019-02-17
    • 1970-01-01
    • 1970-01-01
    • 2018-12-24
    • 2019-01-07
    相关资源
    最近更新 更多