Skip to content

Commit 1ba3faa

Browse files
committed
fix: 修复含通配符时的事件类型匹配
1 parent 4581eab commit 1ba3faa

File tree

4 files changed

+173
-10
lines changed

4 files changed

+173
-10
lines changed

packages/native/src/__tests__/types/transform.type.test.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import { describe, test, expect } from 'vitest';
44
import type { Equal, Expect } from '@type-challenges/utils';
55
import { FastEvent } from '../../event';
6-
import { FastEventMessage, FastEventListener, TypedFastEventListener, AssertFastMessage as NotPayload, PickTransformedEvents } from '../../types';
6+
import { AssertFastMessage as NotPayload, PickTransformedEvents, TransformedEvents, RecordValues, ClosestWildcardEvents } from '../../types';
7+
78

89
describe('启用Transform时的类型支持', () => {
910
test('自定义事件转换', () => {
@@ -154,4 +155,46 @@ describe('启用Transform时的类型支持', () => {
154155
resolve();
155156
});
156157
});
158+
test('所有Scope自定义事件转换', () => {
159+
type WebEvents = TransformedEvents<{
160+
'rooms/*/$join': { room: string; welcome: string; users: string[] }
161+
'rooms/*/$leave': string
162+
'rooms/*/$error': string
163+
'rooms/*/$add': string
164+
'rooms/*/$remove': string
165+
'rooms/*/*': number
166+
}>
167+
const emitter = new FastEvent<WebEvents>()
168+
emitter.on('rooms/xssss/$join', (message) => {
169+
type cases = [
170+
Expect<Equal<typeof message.room, string>>,
171+
Expect<Equal<typeof message.welcome, string>>,
172+
Expect<Equal<typeof message.users, string[]>>
173+
]
174+
})
175+
emitter.once('rooms/xssss/$join', (message) => {
176+
type cases = [
177+
Expect<Equal<typeof message.room, string>>,
178+
Expect<Equal<typeof message.welcome, string>>,
179+
Expect<Equal<typeof message.users, string[]>>
180+
]
181+
})
182+
const scope = emitter.scope('rooms/test')
183+
type ScopeEvents = typeof scope.types.events
184+
type JoinEvents = ScopeEvents['$join']['type']
185+
type SJoinEventss = ClosestWildcardEvents<ScopeEvents, '$join'>
186+
type JoinEventss = RecordValues<ClosestWildcardEvents<ScopeEvents, '$join'>>
187+
188+
189+
scope.on('$join', (message) => {
190+
message.room
191+
message.welcome
192+
message.users
193+
type cases = [
194+
Expect<Equal<typeof message.room, string>>,
195+
Expect<Equal<typeof message.welcome, string>>,
196+
Expect<Equal<typeof message.users, string[]>>
197+
]
198+
})
199+
})
157200
});

packages/native/src/types/FirstObjectItem.ts

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,70 @@ import { Keys } from './Keys';
44
* 保留对象中第一项
55
*/
66
export type FirstObjectItem<T extends Record<string, any>> = Pick<T, Keys<T> extends any[] ? Keys<T>[0] : never>;
7+
8+
// type isEmpty<T extends Record<string, any>> = keyof T extends never ? true : false;
9+
// // 获取所有Key包括通配符字符*的项
10+
// export type GetWildcardItems<T extends Record<string, any>> = {
11+
// [K in keyof T as K extends `${string}*${string}` ? K : K extends `*` ? K : never]: T[K];
12+
// }
713

8-
// type Events = {
9-
// 'rooms/*/join': string;
10-
// 'rooms/*/*': number;
11-
// };
14+
// export type GetNotWildcardItems<T extends Record<string, any>> = {
15+
// [K in keyof T as K extends `${string}*${string}` ? never : K extends `*` ? never : K]: T[K];
16+
// }
1217

13-
// type R = FirstObjectItem<Events>
18+
// export type GetFirstMatchedItem<T extends Record<string, any>> =FirstObjectItem<isEmpty<GetNotWildcardItems<T>> extends true
19+
// ? GetWildcardItems<T>
20+
// : GetNotWildcardItems<T>>
1421

15-
type d = Pick<{ a: 1 }, never>;
22+
23+
// // type Events = {
24+
// // 'rooms/*/$join': { room: string; welcome: string; users: string[] }
25+
// // 'rooms/*/$leave': string
26+
// // 'rooms/*/$error': string
27+
// // 'rooms/*/$add': string
28+
// // 'rooms/*/$remove': string
29+
// // 'rooms/*/*': number
30+
// // };
31+
// type F1 =GetWildcardItems<{
32+
// 'a': string
33+
// 'b': number
34+
// 'rooms/*/$join': number
35+
// 'c': number
36+
// '*': string
37+
// }>
38+
// type F2 =GetNotWildcardItems<{
39+
// 'a': string
40+
// 'b': number
41+
// 'rooms/*/$join': number
42+
// 'c': number
43+
// '*': string
44+
// }>
45+
46+
// type f1 = GetFirstMatchedItem<{
47+
// 'rooms/*/$join': number
48+
// 'rooms/a/$join': string
49+
// }>
50+
// type f2 = GetFirstMatchedItem<{
51+
// 'rooms/a/$join': string
52+
// 'rooms/*/$join': number
53+
// }>
54+
55+
// type f3 = GetFirstMatchedItem<{
56+
// 'rooms/a/$join': string
57+
// 'rooms/b/$join': number
58+
// 'rooms/c/$join': number
59+
// '*': string
60+
// }>
61+
// type f4 = GetFirstMatchedItem<{
62+
// 'a': string
63+
// 'b': number
64+
// 'c': number
65+
// '*': string
66+
// }>
67+
// type f5 = GetFirstMatchedItem<{
68+
// 'rooms/a/$join': string
69+
// 'rooms/b/$join': number
70+
// 'rooms/c/$join': number
71+
// '*': string
72+
// }>
73+
// type f6 = GetFirstMatchedItem<{}>

packages/native/src/types/Keys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ type LastOfUnion<T> = UnionToIntersection<T extends any ? (x: T) => 0 : never> e
99
// 将联合类型转换为交叉类型
1010
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
1111
export type Keys<T extends Record<string, any>> = UnionToTuple<keyof T>;
12+

packages/native/src/types/WildcardEvents.ts

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FirstObjectItem } from './FirstObjectItem';
1+
import { Keys } from './Keys';
22

33
// MergeUnion<{ a: 1 } | { b: 2 }> === { a: 1, b: 2 }
44
export type MergeUnion<T> = (T extends any ? (x: T) => void : never) extends (x: infer U) => void ? { [K in keyof U]: U[K] } : never;
@@ -39,6 +39,9 @@ type Fallback<T, F> = [T] extends [never]
3939
? F // 处理undefined情况
4040
: T; // 否则返回原类型
4141

42+
43+
44+
4245
/**
4346
*
4447
* 返回所有匹配事件的类型
@@ -64,7 +67,65 @@ export type WildcardEvents<Events extends Record<string, any>, T extends string>
6467
}
6568
>
6669
>;
70+
71+
72+
/**
73+
* 保留对象中第一项
74+
*/
75+
type FirstObjectItem<T extends Record<string, any>> = Pick<T, Keys<T> extends any[] ? Keys<T>[0] : never>;
76+
77+
type isEmpty<T extends Record<string, any>> = keyof T extends never ? true : false;
78+
// 获取所有Key包括通配符字符*的项
79+
type GetWildcardItems<T extends Record<string, any>> = {
80+
[K in keyof T as K extends `${string}*${string}` ? K : K extends `*` ? K : never]: T[K];
81+
}
82+
83+
export type GetNotWildcardItems<T extends Record<string, any>> = {
84+
[K in keyof T as K extends `${string}*${string}` ? never : K extends `*` ? never : K]: T[K];
85+
}
86+
87+
export type GetFirstMatchedItem<T extends Record<string, any>> =FirstObjectItem<isEmpty<GetNotWildcardItems<T>> extends true
88+
? GetWildcardItems<T>
89+
: GetNotWildcardItems<T>>
90+
91+
6792
// 只返回最相近匹配的事件类型
68-
export type ClosestWildcardEvents<Events extends Record<string, any>, T extends string> = FirstObjectItem<WildcardEvents<Events, T>>;
93+
export type ClosestWildcardEvents<Events extends Record<string, any>, T extends string>
94+
= GetFirstMatchedItem<WildcardEvents<Events, T>>
95+
96+
97+
98+
99+
100+
101+
// type Events2 = ClosestWildcardEvents<{
102+
// '$join': { room: string; welcome: string; users: string[] }
103+
// '$leave': string
104+
// '$error': string
105+
// '$add': string
106+
// '$remove': string
107+
// '*': number
108+
// },'$join'>
109+
110+
111+
112+
// type WebEvents = ClosestWildcardEvents<{
113+
// 'rooms/*/$join': { room: string; welcome: string; users: string[] }
114+
// 'rooms/*/$leave': string
115+
// 'rooms/*/$error': string
116+
// 'rooms/*/$add': string
117+
// 'rooms/*/$remove': string
118+
// 'rooms/*/*': number
119+
// },'rooms/*/$join'>
120+
121+
69122

70-
type s = ClosestWildcardEvents<any, 'xxx'>;
123+
// type Events = ClosestWildcardEvents<{
124+
// '$join': { room: string; welcome: string; users: string[] }
125+
// '$leave': string
126+
// '$error': string
127+
// '$add': string
128+
// '$remove': string
129+
// '*': number
130+
// },'$join'>
131+

0 commit comments

Comments
 (0)