【问题标题】:Filtering keyof in typescript mapped types works unexpectedly在打字稿映射类型中过滤 keyof 意外工作
【发布时间】:2022-01-21 05:39:10
【问题描述】:

我正在尝试提取与事件处理程序模式匹配的接口的键(使用keyof) - 即(CustomEvent) => void

我有一个似乎有效的方法,它只提取与(CustomEvent) => void 匹配的键,但是,它也提取类型为() => void 的键。

有没有办法只提取符合(CustomEvent) => void 的类型的键?

方法基于问题 Filter interface keys for a sub-list of keys based on value typeTypescript : Filter on keyof type parameter

在下面的例子中

export interface JayCustomEvent {}

type EventHandler = (e: JayCustomEvent) => void;

type FilteredKeys<T, U> = { [P in keyof T]: P extends string ? (T[P] extends U ? P : never) : never}[keyof T];

interface AComponentEvent extends JayCustomEvent {}
interface AComponent {
    anEvent(e: AComponentEvent): void,
    noParamFunction(): void,
    someOtherFunction(a: number): void
    someProp: string
}

let a: FilteredKeys<AComponent, EventHandler> = 'anEvent'
let b: FilteredKeys<AComponent, EventHandler> = 'noParamFunction'
let c: FilteredKeys<AComponent, EventHandler> = 'someOtherFunction'
let d: FilteredKeys<AComponent, EventHandler> = 'someProp'

我希望分配 a 能够正常工作,而 bcd 不应该。 但是,分配 ab 有效,而只有 cd 无效。

【问题讨论】:

    标签: typescript generics


    【解决方案1】:

    您的地图工具看起来不错。

    我想你可能会惊讶地发现functions with lower arity are subtypes of compatible higher-arity ones。考虑以下示例:

    type AssignableTo<T, U> = T extends U ? true : false;
    
    type Fn0Params = () => void;
    type Fn1Param = (p: unknown) => void;
    
    declare const ex1: AssignableTo<Fn0Params, Fn1Param>;
    ex1 // true
    
    type Fn0ParamsNever = (...args: never) => void;
    
    declare const ex2: AssignableTo<Fn0ParamsNever, Fn1Param>;
    ex2 // false
    

    因此,通过将noParamFunction 输入为具有never 类型的参数,您可以防止将其分配给EventHandler

    noParamFunction(...args: never): void
    

    TS Playground

    根据您的问题:如果您实际使用的是CustomEvents,那么您可能想要使用(event: CustomEvent&lt;JayCustomEvent&gt;) =&gt; void

    【讨论】:

      【解决方案2】:

      我已经完全解决了我需要的问题,所以在这里发帖以帮助其他人。

      根据定义

      type Func0 = () => void
      type Func1 = (x: any) => void
      type DOMeventHandler<E> = (((this: GlobalEventHandlers, ev: E) => any) | null)
      

      正如@jsejcksn 在他的回答中指出的那样(这是正确的),Func0 确实扩展了Func1。但是,我们可以使用它来构造一个与作为一个参数函数的对象的键完全匹配的类型。

      答案就在这里,下面解释

      type EventHandlerKeys<T> = {
          [P in keyof T]:
          P extends string ?
              (T[P] extends Func1 ?
                  (T[P] extends Func0 ? never : P) :
                  T[P] extends DOMeventHandler<any> ? P : never) :
              never
      }[keyof T];
      

      诀窍是匹配Func1,它选择零参数或一个参数的两个函数。然后我们再次匹配Func0,对于零参数函数返回never,对于一参数函数返回实际属性类型。

      在这里,我们还匹配 DOMEventHandler 作为另一个条件分支的额外奖励。

      【讨论】:

        猜你喜欢
        • 2020-08-13
        • 1970-01-01
        • 2019-11-19
        • 1970-01-01
        • 1970-01-01
        • 2022-12-07
        • 2022-08-21
        • 2018-06-16
        • 2022-08-17
        相关资源
        最近更新 更多