在打字稿映射类型中过滤 keyof 意外工作
Posted
技术标签:
【中文标题】在打字稿映射类型中过滤 keyof 意外工作【英文标题】:Filtering keyof in typescript mapped types works unexpectedly 【发布时间】: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 type 和 Typescript : 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
能够正常工作,而 b
、c
和 d
不应该。
但是,分配 a
和 b
有效,而只有 c
和 d
无效。
【问题讨论】:
【参考方案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
根据您的问题:如果您实际使用的是
CustomEvent
s,那么您可能想要使用(event: CustomEvent<JayCustomEvent>) => 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
作为另一个条件分支的额外奖励。
【讨论】:
以上是关于在打字稿映射类型中过滤 keyof 意外工作的主要内容,如果未能解决你的问题,请参考以下文章
打字稿:如何创建 Array 泛型类型,其中包含具有给定接口的每个 keyof 实例的对象