Skip to content

Commit aa523b7

Browse files
authored
Improve gameplay utilities nextEvent type (#102)
1 parent 40d1475 commit aa523b7

File tree

3 files changed

+43
-23
lines changed

3 files changed

+43
-23
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "minor",
3+
"comment": "feat: improve type infer",
4+
"packageName": "@minecraft/gameplay-utilities",
5+
"email": "renixiel@gmail.com",
6+
"dependentChangeType": "patch"
7+
}

libraries/gameplay-utilities/api-report/gameplay-utilities.api.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
55
```ts
66

7-
import { system } from '@minecraft/server';
8-
import { world } from '@minecraft/server';
7+
import { SystemAfterEvents } from '@minecraft/server';
8+
import { WorldAfterEvents } from '@minecraft/server';
99

1010
// @public
1111
export interface EventPromise<T> extends Promise<T | undefined> {
@@ -16,13 +16,13 @@ export interface EventPromise<T> extends Promise<T | undefined> {
1616
}
1717

1818
// @public
19-
export type FirstArg<T> = T extends (arg: infer U) => void ? U : never;
19+
export type MinecraftAfterEventSignalKeys = keyof WorldAfterEvents | keyof SystemAfterEvents;
2020

2121
// @public
22-
export type MinecraftAfterEventSignals = (typeof world.afterEvents)[keyof typeof world.afterEvents] | (typeof system.afterEvents)[keyof typeof system.afterEvents];
22+
export type MinecraftAfterEventSignals<K extends MinecraftAfterEventSignalKeys> = K extends keyof WorldAfterEvents ? WorldAfterEvents[K] : K extends keyof SystemAfterEvents ? SystemAfterEvents[K] : never;
2323

2424
// @public
25-
export function nextEvent<U>(signal: MinecraftAfterEventSignals, filter?: U): EventPromise<FirstArg<FirstArg<typeof signal.subscribe>>>;
25+
export function nextEvent<T extends MinecraftAfterEventSignals<MinecraftAfterEventSignalKeys>>(signal: T, filter?: Parameters<T['subscribe']>[1]): EventPromise<Parameters<ReturnType<T['subscribe']>>[0]>;
2626

2727
// (No @packageDocumentation comment for this package)
2828

libraries/gameplay-utilities/src/events/eventPromise.ts

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
import { world, system } from '@minecraft/server';
4+
import { SystemAfterEvents, WorldAfterEvents } from '@minecraft/server';
55

66
/**
77
* A promise wrapper utility which returns a new promise that will resolve when the next
@@ -49,25 +49,33 @@ export interface EventPromise<T> extends Promise<T | undefined> {
4949
finally(onfinally?: (() => void) | null): Promise<T | undefined>;
5050
}
5151

52+
/**
53+
* The keys of after event signals that exist in Minecraft's API that EventPromise can use.
54+
*
55+
* @public
56+
*/
57+
export type MinecraftAfterEventSignalKeys = keyof WorldAfterEvents | keyof SystemAfterEvents;
5258
/**
5359
* The types of after event signals that exist in Minecraft's API that EventPromise can use.
5460
*
5561
* @public
5662
*/
57-
export type MinecraftAfterEventSignals =
58-
| (typeof world.afterEvents)[keyof typeof world.afterEvents]
59-
| (typeof system.afterEvents)[keyof typeof system.afterEvents];
63+
export type MinecraftAfterEventSignals<K extends MinecraftAfterEventSignalKeys> = K extends keyof WorldAfterEvents
64+
? WorldAfterEvents[K]
65+
: K extends keyof SystemAfterEvents
66+
? SystemAfterEvents[K]
67+
: never;
6068

6169
/**
6270
* Helper to create a new EventPromise from an after event signal.
6371
*
6472
* @public
6573
*/
66-
export function nextEvent<U>(
67-
signal: MinecraftAfterEventSignals,
68-
filter?: U
69-
): EventPromise<Parameters<Parameters<typeof signal.subscribe>[0]>[0]> {
70-
return new EventPromiseImpl<Parameters<Parameters<typeof signal.subscribe>[0]>[0], U>(signal, filter);
74+
export function nextEvent<T extends MinecraftAfterEventSignals<MinecraftAfterEventSignalKeys>>(
75+
signal: T,
76+
filter?: Parameters<T['subscribe']>[1]
77+
): EventPromise<Parameters<ReturnType<T['subscribe']>>[0]> {
78+
return new EventPromiseImpl(signal, filter);
7179
}
7280

7381
/**
@@ -76,32 +84,37 @@ export function nextEvent<U>(
7684
*
7785
* @private
7886
*/
79-
class EventPromiseImpl<T, U> implements Promise<T | undefined> {
87+
class EventPromiseImpl<
88+
K extends MinecraftAfterEventSignalKeys,
89+
Signal extends MinecraftAfterEventSignals<K>,
90+
T = Parameters<ReturnType<Signal['subscribe']>>[0],
91+
U = Parameters<Signal['subscribe']>[1],
92+
> implements EventPromise<T | undefined>
93+
{
8094
[Symbol.toStringTag] = 'Promise';
8195
private promise: Promise<T | undefined>;
8296
private onCancel?: () => void;
8397

84-
constructor(signal: MinecraftAfterEventSignals, filter?: U) {
98+
constructor(signal: Signal, filter?: U) {
8599
this.promise = new Promise<T | undefined>((resolve, _) => {
86100
if (signal === undefined || signal.subscribe === undefined || signal.unsubscribe === undefined) {
87101
resolve(undefined);
88102
return;
89103
}
90104

91-
const sub = (event: T) => {
105+
const sub = (event: Parameters<ReturnType<Signal['subscribe']>>[0]) => {
92106
this.onCancel = undefined;
93-
signal.unsubscribe(sub as never);
94-
resolve(event);
107+
signal.unsubscribe(sub);
108+
resolve(event as T);
95109
};
96110
if (filter === undefined) {
97-
signal.subscribe(sub as never);
111+
signal.subscribe(sub);
98112
} else {
99-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
100-
(signal.subscribe as (listener: (event: T) => void, filter: U) => (...a: any) => void)(sub, filter);
113+
(signal.subscribe as (handler: Parameters<Signal['subscribe']>[0], filter: U) => void)(sub, filter);
101114
}
102115

103116
this.onCancel = () => {
104-
signal.unsubscribe(sub as never);
117+
signal.unsubscribe(sub);
105118
resolve(undefined);
106119
};
107120
});

0 commit comments

Comments
 (0)