Skip to content

Commit

Permalink
Fix: Update types to support union-type arguments (Fixes #10)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
demurgos committed Jan 23, 2018
1 parent 4dfcef3 commit eae2f4c
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 16 deletions.
31 changes: 16 additions & 15 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
declare namespace PluginError {
export interface Constructor {
interface Constructor {
/**
* @param options Options with plugin name and message
* @param plugin Plugin name
* @param error Base error
* @param options Error options
*/
new(options: Options & {plugin: string, message: string}): PluginError;
new <E extends Error>(plugin: string, error: E, options?: Options): PluginError<E>;

/**
* @param plugin Plugin name
* @param message Error message
* @param error Base error or error message
* @param options Error options
*/
new (plugin: string, message: string, options?: Options): PluginError;
new <E extends Error = Error>(plugin: string, error: E | string, options: Options): PluginError<E | {[K in keyof E]: undefined}>;

/**
* @param plugin Plugin name
* @param error Base error
* @param options Error options
* @param error Base error, error message, or options with message
*/
new <E extends Error>(plugin: string, error: E, options?: Options): PluginError<E>;
new <E extends Error = Error>(plugin: string, error: E | string | (Options & {message: string})): PluginError<E | {[K in keyof E]: undefined}>;

/**
* @param plugin Plugin name
* @param options Options with message
* @param options Options with plugin name and message
*/
new(plugin: string, options: Options & {message: string}): PluginError;
new(options: Options & {plugin: string, message: string}): PluginError;
}

interface Options {
Expand Down Expand Up @@ -71,11 +71,12 @@ declare namespace PluginError {
stack?: string;
}


/**
* The `Base` interface defines the properties available on all the the instances of `PluginError`.
* The `SimplePluginError` interface defines the properties available on all the the instances of `PluginError`.
*
* @internal
*/
export interface Base extends Error {
interface SimplePluginError extends Error {
/**
* Plugin name
*/
Expand Down Expand Up @@ -106,7 +107,7 @@ declare namespace PluginError {
/**
* Abstraction for error handling for Vinyl plugins
*/
type PluginError<T = {}> = PluginError.Base & T;
type PluginError<T = {}> = PluginError.SimplePluginError & T;

declare const PluginError: PluginError.Constructor;

Expand Down
87 changes: 86 additions & 1 deletion test/types/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import PluginError = require("plugin-error");

{
const existingError = new Error("OMG");
const err = new PluginError("test", existingError, {showStack: true});
const options: PluginError.Options = {showStack: true};
const err = new PluginError("test", existingError, options);
}
}

Expand All @@ -51,3 +52,87 @@ import PluginError = require("plugin-error");
}
}

{
// Union types
const PLUGIN_NAME: string = "test";

interface FooError extends Error {
foo: number;
}

const ERROR: Error = new Error("something broke");
const FOO_ERROR: FooError = Object.assign(new Error("something broke"), {foo: 1});
const MESSAGE: string = "something broke";
const OPTIONS: {message: string} = {message: "something broke"};

{
function createError(error: Error | string) {
return new PluginError(PLUGIN_NAME, error);
}
const pluginError1 = createError(ERROR);
const pluginError2 = createError(FOO_ERROR);
const pluginError3 = createError(MESSAGE);
// The following code should cause a compilation error:
// const foo: any = pluginError2.foo;
}

{
// Make sure that custom properties are preserved
function createError(error: FooError) {
return new PluginError(PLUGIN_NAME, error);
}
const pluginError = createError(FOO_ERROR);
const foo: number = pluginError.foo;
}

{
// Just check that there's no issue when building it with a string
function createError(error: string) {
return new PluginError(PLUGIN_NAME, error);
}
const pluginError = createError(MESSAGE);
// The following code should cause a compilation error:
// const foo: any = pluginError.foo;
}

{
// Check that custom properties are preserved but marked as potentially missing
// The `foo` property must be of type `number | undefined` because it's existence depends
// on the way `createError` is called.
function createError(error: FooError | string) {
return new PluginError(PLUGIN_NAME, error);
}

const pluginError1 = createError(FOO_ERROR);
const foo1: number | undefined = pluginError1.foo;
// The following code should cause a compilation error:
// const foo2: number = pluginError1.foo;

const pluginError2 = createError(MESSAGE);
const foo3: number | undefined = pluginError2.foo;
// The following code should cause a compilation error:
// const foo4: number = pluginError2.foo;
}

{
// Check support for unions with option object
function createError(error: FooError | string | (PluginError.Options & {message: string})) {
return new PluginError(PLUGIN_NAME, error);
}

const pluginError1 = createError(FOO_ERROR);
const foo1: number | undefined = pluginError1.foo;
// The following code should cause a compilation error:
// const foo2: number = pluginError1.foo;

const pluginError2 = createError(MESSAGE);
const foo3: number | undefined = pluginError2.foo;
// The following code should cause a compilation error:
// const foo4: number = pluginError2.foo;

const pluginError3 = createError(OPTIONS);
const foo5: number | undefined = pluginError3.foo;
// The following code should cause a compilation error:
// const foo6: number = pluginError3.foo;
}
}

0 comments on commit eae2f4c

Please sign in to comment.