From 3fa04b55f19adf01885e76fcff40ee22fde0a49d Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:10:59 +0000 Subject: [PATCH 1/2] fix: strongly type `unlink` events The `unlink` and `unlinkDir` events both have known types, so this adds them to the known event map. --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index 45666552..4fc2ad56 100644 --- a/src/index.ts +++ b/src/index.ts @@ -303,6 +303,8 @@ export interface FSWatcherKnownEventMap { [EV.RAW]: Parameters; [EV.ERROR]: Parameters; [EV.ALL]: [EventName, ...EmitArgs]; + [EV.UNLINK]: [path: string]; + [EV.UNLINK_DIR]: [path: string]; } export type FSWatcherEventMap = FSWatcherKnownEventMap & { From 322e851f4564c7346b84fac6bdfe4e32c4095d99 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Wed, 18 Dec 2024 21:56:21 +0000 Subject: [PATCH 2/2] fix: separate error emit from regular events Separates the error event arguments from the regular event arguments. Before, we had a merged tuple of `[Path | Error, Stats?]` to account for the fact that some events can be `[Path, Stats?]` and some can be `[Error, Stats?]`. However, this results in incorrect types since nothing will ever pass `Path | Error` as a type (only one or the other). This separates them and uses a union instead, such that event handlers other than `error` only ever have a `Path`. --- src/index.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4fc2ad56..46b4bc92 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,8 +60,9 @@ export type FSWInstanceOptions = BasicOpts & { }; export type ThrottleType = 'readdir' | 'watch' | 'add' | 'remove' | 'change'; -export type EmitArgs = [Path | Error, Stats?]; -export type EmitArgsWithName = [EventName, ...EmitArgs]; +export type EmitArgs = [path: Path, stats?: Stats]; +export type EmitErrorArgs = [error: Error, stats?: Stats]; +export type EmitArgsWithName = [event: EventName, ...EmitArgs]; export type MatchFunction = (val: string, stats?: Stats) => boolean; export interface MatcherObject { path: string; @@ -302,9 +303,7 @@ export interface FSWatcherKnownEventMap { [EV.READY]: []; [EV.RAW]: Parameters; [EV.ERROR]: Parameters; - [EV.ALL]: [EventName, ...EmitArgs]; - [EV.UNLINK]: [path: string]; - [EV.UNLINK_DIR]: [path: string]; + [EV.ALL]: [event: EventName, ...EmitArgs]; } export type FSWatcherEventMap = FSWatcherKnownEventMap & { @@ -604,7 +603,7 @@ export class FSWatcher extends EventEmitter { const opts = this.options; if (isWindows) path = sysPath.normalize(path); if (opts.cwd) path = sysPath.relative(opts.cwd, path); - const args: EmitArgs = [path]; + const args: EmitArgs | EmitErrorArgs = [path]; if (stats != null) args.push(stats); const awf = opts.awaitWriteFinish; @@ -639,7 +638,7 @@ export class FSWatcher extends EventEmitter { const awfEmit = (err?: Error, stats?: Stats) => { if (err) { event = EV.ERROR; - args[0] = err; + (args as unknown as EmitErrorArgs)[0] = err; this.emitWithAll(event, args); } else if (stats) { // if stats doesn't exist the file must have been deleted