Skip to content

Commit eae2f4c

Browse files
authored
Fix: Update types to support union-type arguments (Fixes #10)
This commit updates the signature of the `PluginError` constructor to loosen the constraints on the parameters in order to support union types while keeping inference on custom properties. This is a semver minor update (fix). This allows for example to use the pattern below: ``` function createPluginError(error: Error | string) { return new PluginError("test", error); } ``` (The tests were updated with more complex cases) See the discussions in #10 and #11 for details. - Closes #10 - Closes #11 /cc @gucong3000
1 parent 4dfcef3 commit eae2f4c

File tree

2 files changed

+102
-16
lines changed

2 files changed

+102
-16
lines changed

index.d.ts

+16-15
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
declare namespace PluginError {
2-
export interface Constructor {
2+
interface Constructor {
33
/**
4-
* @param options Options with plugin name and message
4+
* @param plugin Plugin name
5+
* @param error Base error
6+
* @param options Error options
57
*/
6-
new(options: Options & {plugin: string, message: string}): PluginError;
8+
new <E extends Error>(plugin: string, error: E, options?: Options): PluginError<E>;
79

810
/**
911
* @param plugin Plugin name
10-
* @param message Error message
12+
* @param error Base error or error message
1113
* @param options Error options
1214
*/
13-
new (plugin: string, message: string, options?: Options): PluginError;
15+
new <E extends Error = Error>(plugin: string, error: E | string, options: Options): PluginError<E | {[K in keyof E]: undefined}>;
1416

1517
/**
1618
* @param plugin Plugin name
17-
* @param error Base error
18-
* @param options Error options
19+
* @param error Base error, error message, or options with message
1920
*/
20-
new <E extends Error>(plugin: string, error: E, options?: Options): PluginError<E>;
21+
new <E extends Error = Error>(plugin: string, error: E | string | (Options & {message: string})): PluginError<E | {[K in keyof E]: undefined}>;
2122

2223
/**
23-
* @param plugin Plugin name
24-
* @param options Options with message
24+
* @param options Options with plugin name and message
2525
*/
26-
new(plugin: string, options: Options & {message: string}): PluginError;
26+
new(options: Options & {plugin: string, message: string}): PluginError;
2727
}
2828

2929
interface Options {
@@ -71,11 +71,12 @@ declare namespace PluginError {
7171
stack?: string;
7272
}
7373

74-
7574
/**
76-
* The `Base` interface defines the properties available on all the the instances of `PluginError`.
75+
* The `SimplePluginError` interface defines the properties available on all the the instances of `PluginError`.
76+
*
77+
* @internal
7778
*/
78-
export interface Base extends Error {
79+
interface SimplePluginError extends Error {
7980
/**
8081
* Plugin name
8182
*/
@@ -106,7 +107,7 @@ declare namespace PluginError {
106107
/**
107108
* Abstraction for error handling for Vinyl plugins
108109
*/
109-
type PluginError<T = {}> = PluginError.Base & T;
110+
type PluginError<T = {}> = PluginError.SimplePluginError & T;
110111

111112
declare const PluginError: PluginError.Constructor;
112113

test/types/test.ts

+86-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import PluginError = require("plugin-error");
2626

2727
{
2828
const existingError = new Error("OMG");
29-
const err = new PluginError("test", existingError, {showStack: true});
29+
const options: PluginError.Options = {showStack: true};
30+
const err = new PluginError("test", existingError, options);
3031
}
3132
}
3233

@@ -51,3 +52,87 @@ import PluginError = require("plugin-error");
5152
}
5253
}
5354

55+
{
56+
// Union types
57+
const PLUGIN_NAME: string = "test";
58+
59+
interface FooError extends Error {
60+
foo: number;
61+
}
62+
63+
const ERROR: Error = new Error("something broke");
64+
const FOO_ERROR: FooError = Object.assign(new Error("something broke"), {foo: 1});
65+
const MESSAGE: string = "something broke";
66+
const OPTIONS: {message: string} = {message: "something broke"};
67+
68+
{
69+
function createError(error: Error | string) {
70+
return new PluginError(PLUGIN_NAME, error);
71+
}
72+
const pluginError1 = createError(ERROR);
73+
const pluginError2 = createError(FOO_ERROR);
74+
const pluginError3 = createError(MESSAGE);
75+
// The following code should cause a compilation error:
76+
// const foo: any = pluginError2.foo;
77+
}
78+
79+
{
80+
// Make sure that custom properties are preserved
81+
function createError(error: FooError) {
82+
return new PluginError(PLUGIN_NAME, error);
83+
}
84+
const pluginError = createError(FOO_ERROR);
85+
const foo: number = pluginError.foo;
86+
}
87+
88+
{
89+
// Just check that there's no issue when building it with a string
90+
function createError(error: string) {
91+
return new PluginError(PLUGIN_NAME, error);
92+
}
93+
const pluginError = createError(MESSAGE);
94+
// The following code should cause a compilation error:
95+
// const foo: any = pluginError.foo;
96+
}
97+
98+
{
99+
// Check that custom properties are preserved but marked as potentially missing
100+
// The `foo` property must be of type `number | undefined` because it's existence depends
101+
// on the way `createError` is called.
102+
function createError(error: FooError | string) {
103+
return new PluginError(PLUGIN_NAME, error);
104+
}
105+
106+
const pluginError1 = createError(FOO_ERROR);
107+
const foo1: number | undefined = pluginError1.foo;
108+
// The following code should cause a compilation error:
109+
// const foo2: number = pluginError1.foo;
110+
111+
const pluginError2 = createError(MESSAGE);
112+
const foo3: number | undefined = pluginError2.foo;
113+
// The following code should cause a compilation error:
114+
// const foo4: number = pluginError2.foo;
115+
}
116+
117+
{
118+
// Check support for unions with option object
119+
function createError(error: FooError | string | (PluginError.Options & {message: string})) {
120+
return new PluginError(PLUGIN_NAME, error);
121+
}
122+
123+
const pluginError1 = createError(FOO_ERROR);
124+
const foo1: number | undefined = pluginError1.foo;
125+
// The following code should cause a compilation error:
126+
// const foo2: number = pluginError1.foo;
127+
128+
const pluginError2 = createError(MESSAGE);
129+
const foo3: number | undefined = pluginError2.foo;
130+
// The following code should cause a compilation error:
131+
// const foo4: number = pluginError2.foo;
132+
133+
const pluginError3 = createError(OPTIONS);
134+
const foo5: number | undefined = pluginError3.foo;
135+
// The following code should cause a compilation error:
136+
// const foo6: number = pluginError3.foo;
137+
}
138+
}

0 commit comments

Comments
 (0)