Skip to content

Commit

Permalink
Properly split constructor and instance types (#867)
Browse files Browse the repository at this point in the history
* Properly split constructor and instance types

Fixes #803

* Fix a preexisting typo in Well Formed Plugin detection

* Also declare encoders and decoders types

* Update types tests

* Fix custom instance types

* Fix tsTest errors

* Fix typings for typescript 3.1
  • Loading branch information
forivall authored Mar 30, 2020
1 parent 3a3df33 commit e1d05da
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 138 deletions.
128 changes: 65 additions & 63 deletions packages/core/types/jimp.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,7 @@ interface ScanIteratorReturn<This> {
}

export interface JimpConstructors {
new(path: string, cb?: ImageCallback<this>): this;
new(urlOptions: URLOptions, cb?: ImageCallback<this>): this;
new(image: Jimp, cb?: ImageCallback<this>): this;
new(data: Buffer, cb?: ImageCallback<this>): this;
new(data: Bitmap, cb?: ImageCallback<this>): this;
new(w: number, h: number, cb?: ImageCallback<this>): this;
new(
w: number,
h: number,
background?: number | string,
cb?: ImageCallback<this>
): this;
// For custom constructors when using Jimp.appendConstructorOption
new(...args: any[]): this;
}

export interface Jimp extends JimpConstructors {
prototype: this;
prototype: Jimp;
// Constants
AUTO: -1;
// blend modes
Expand All @@ -65,6 +48,70 @@ export interface Jimp extends JimpConstructors {
EDGE_EXTEND: 1;
EDGE_WRAP: 2;
EDGE_CROP: 3;

// Constructors
new(path: string, cb?: ImageCallback<this['prototype']>): this['prototype'];
new(urlOptions: URLOptions, cb?: ImageCallback<this['prototype']>): this['prototype'];
new(image: Jimp, cb?: ImageCallback<this['prototype']>): this['prototype'];
new(data: Buffer, cb?: ImageCallback<this['prototype']>): this['prototype'];
new(data: Bitmap, cb?: ImageCallback<this['prototype']>): this['prototype'];
new(w: number, h: number, cb?: ImageCallback<this['prototype']>): this['prototype'];
new(
w: number,
h: number,
background?: number | string,
cb?: ImageCallback<this['prototype']>
): this['prototype'];
// For custom constructors when using Jimp.appendConstructorOption
new(...args: any[]): this['prototype'];

// Functions
/**
* I'd like to make `Args` generic and used in `run` and `test` but alas,
* it's not possible RN:
* https://github.com/microsoft/TypeScript/issues/26113
*/
appendConstructorOption<Args extends any[], J extends Jimp = this['prototype']>(
name: string,
test: (...args: any[]) => boolean,
run: (
this: J,
resolve: (jimp?: J) => any,
reject: (reason: Error) => any,
...args: any[]
) => any
): void;
read(path: string, cb?: ImageCallback<this['prototype']>): Promise<this['prototype']>;
read(image: Jimp, cb?: ImageCallback<this['prototype']>): Promise<this['prototype']>;
read(data: Buffer, cb?: ImageCallback<this['prototype']>): Promise<this['prototype']>;
read(
w: number,
h: number,
background?: number | string,
cb?: ImageCallback<this['prototype']>
): Promise<this['prototype']>;
create(path: string): Promise<this['prototype']>;
create(image: Jimp): Promise<this['prototype']>;
create(data: Buffer): Promise<this['prototype']>;
create(w: number, h: number, background?: number | string): Promise<this['prototype']>;
rgbaToInt(
r: number,
g: number,
b: number,
a: number,
cb: GenericCallback<number, any, this['prototype']>
): number;
intToRGBA(i: number, cb?: GenericCallback<RGBA>): RGBA;
cssColorToHex(cssColor: string): number;
limit255(n: number): number;
diff(img1: Jimp, img2: Jimp, threshold?: number): DiffReturn<this['prototype']>;
distance(img1: Jimp, img2: Jimp): number;
compareHashes(hash1: string, hash2: string): number;
colorDiff(rgba1: RGB, rgba2: RGB): number;
colorDiff(rgba1: RGBA, rgba2: RGBA): number;
}

export interface Jimp {
// Properties
bitmap: Bitmap;
_rgba: boolean;
Expand Down Expand Up @@ -168,49 +215,4 @@ export interface Jimp extends JimpConstructors {
options?: BlendMode,
cb?: ImageCallback<this>
): this;

// Functions
/**
* I'd like to make `Args` generic and used in `run` and `test` but alas,
* it's not possible RN:
* https://github.com/microsoft/TypeScript/issues/26113
*/
appendConstructorOption<Args extends any[], J extends Jimp = this>(
name: string,
test: (...args: any[]) => boolean,
run: (
this: J,
resolve: (jimp?: J) => any,
reject: (reason: Error) => any,
...args: any[]
) => any
): void;
read(path: string, cb?: ImageCallback<this>): Promise<this>;
read(image: Jimp, cb?: ImageCallback<this>): Promise<this>;
read(data: Buffer, cb?: ImageCallback<this>): Promise<this>;
read(
w: number,
h: number,
background?: number | string,
cb?: ImageCallback<this>
): Promise<this>;
create(path: string): Promise<this>;
create(image: Jimp): Promise<this>;
create(data: Buffer): Promise<this>;
create(w: number, h: number, background?: number | string): Promise<this>;
rgbaToInt(
r: number,
g: number,
b: number,
a: number,
cb: GenericCallback<number, any, this>
): number;
intToRGBA(i: number, cb?: GenericCallback<RGBA>): RGBA;
cssColorToHex(cssColor: string): number;
limit255(n: number): number;
diff(img1: Jimp, img2: Jimp, threshold?: number): DiffReturn<this>;
distance(img1: Jimp, img2: Jimp): number;
compareHashes(hash1: string, hash2: string): number;
colorDiff(rgba1: RGB, rgba2: RGB): number;
colorDiff(rgba1: RGBA, rgba2: RGBA): number;
}
65 changes: 58 additions & 7 deletions packages/core/types/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ export type UnionToIntersection<U> =

/**
* The values to be extracted from a WellFormedPlugin to put onto the Jimp instance
* Left loose as "any" in order to enable the GetPluginValue to work properly
* Left loose as "any" in order to enable the GetPluginVal to work properly
*/
export type WellFormedValues<T extends any> =
(T extends {class: any} ? T['class'] : {}) &
(T extends {constants: any} ? T['constants'] : {});
(T extends {class: infer Class} ? Class : {});

/**
* The constants to be extracted from a WellFormedPlugin to put onto the Jimp instance
* Left loose as "any" in order to enable the GetPluginConstants to work properly
*/
export type WellFormedConstants<T extends any> =
(T extends {constants: infer Constants} ? Constants : {});

// Util type for the functions that deal with `@jimp/custom`
// Must accept any or no props thanks to typing of the `plugins` intersected function
Expand All @@ -26,25 +32,70 @@ export type FunctionRet<T> = Array<(...props: any[] | never) => T>;
* up `undefined`. Because we're always extending `IllformedPlugin` on the
* plugins, this should work fine
*/
export type GetPluginVal<Q> = Q extends Required<{class: any}> | Required<{constant: any}>
export type GetPluginVal<Q> = Q extends Required<{class: any}> | Required<{constants: any}>
? WellFormedValues<Q>
: Q;

export type GetPluginConst<Q> = Q extends Required<{class: any}> | Required<{constants: any}>
? WellFormedConstants<Q>
: {};

export type GetPluginDecoders<Q> = Q extends Required<{class: any}> | Required<{constants: any}>
? Q extends {decoders: infer Decoders} ? Decoders : {} : {};

export type GetPluginEncoders<Q> = Q extends Required<{class: any}> | Required<{constants: any}>
? Q extends {encoders: infer Encoders} ? Encoders : {} : {};

type GetPluginFuncArrValues<PluginFuncArr> =
// Given an array of types infer `Q` (Q should be the type value)
PluginFuncArr extends Array<() => infer Q>
PluginFuncArr extends ReadonlyArray<infer F> ? F extends () => infer Q
? // Get the plugin value, may be ill-formed or well-formed
GetPluginVal<Q>
: // This should never be reached
undefined;
undefined : undefined;

/**
* A helper type to get the values to be intersected with `Jimp` to give
* the proper typing given an array of functions for plugins and types
*/
export type GetIntersectionFromPlugins<
PluginFuncArr extends FunctionRet<JimpPlugin | JimpType>
> = UnionToIntersection<GetPluginFuncArrValues<PluginFuncArr>>;
> = UnionToIntersection<Exclude<GetPluginFuncArrValues<PluginFuncArr>, undefined>>;

type GetPluginFuncArrConsts<PluginFuncArr> =
// Given an array of types infer `Q` (Q should be the type value)
PluginFuncArr extends ReadonlyArray<infer F> ? F extends () => infer Q
? // Get the plugin constants, may be ill-formed or well-formed
GetPluginConst<Q>
: // This should never be reached
undefined : undefined;

type GetPluginFuncArrEncoders<PluginFuncArr> =
// Given an array of types infer `Q` (Q should be the type value)
PluginFuncArr extends ReadonlyArray<infer F> ? F extends () => infer Q
? // Get the plugin encoders, may be ill-formed or well-formed
GetPluginEncoders<Q>
: // This should never be reached
undefined : undefined;

type GetPluginFuncArrDecoders<PluginFuncArr> =
// Given an array of types infer `Q` (Q should be the type value)
PluginFuncArr extends ReadonlyArray<infer F> ? F extends () => infer Q
? // Get the plugin decoders, may be ill-formed or well-formed
GetPluginDecoders<Q>
: // This should never be reached
undefined : undefined;

/**
* A helper type to get the statics to be intersected with `Jimp` to give
* the proper typing given an array of functions for plugins and types
*/
export type GetIntersectionFromPluginsStatics<
PluginFuncArr extends FunctionRet<JimpPlugin | JimpType>
> = UnionToIntersection<GetPluginFuncArrConsts<PluginFuncArr>> & {
encoders: UnionToIntersection<GetPluginFuncArrEncoders<PluginFuncArr>>;
decoders: UnionToIntersection<GetPluginFuncArrDecoders<PluginFuncArr>>;
};

/**
* While this was added to TS 3.5, in order to support down to TS 2.8, we need
Expand Down
11 changes: 6 additions & 5 deletions packages/custom/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@ import {
JimpPlugin,
JimpType,
GetIntersectionFromPlugins,
GetIntersectionFromPluginsStatics,
JimpConstructors
} from '@jimp/core';

type JimpInstance<
TypesFuncArr extends FunctionRet<JimpType> | undefined,
PluginFuncArr extends FunctionRet<JimpPlugin> | undefined,
J extends Jimp
> = Exclude<J, undefined> &
GetIntersectionFromPlugins<Exclude<TypesFuncArr | PluginFuncArr, undefined>> &
JimpConstructors;
J extends JimpConstructors
> = J & GetIntersectionFromPluginsStatics<Exclude<TypesFuncArr | PluginFuncArr, undefined>> & {
prototype: JimpType & GetIntersectionFromPlugins<Exclude<TypesFuncArr | PluginFuncArr, undefined>>
};

declare function configure<
TypesFuncArr extends FunctionRet<JimpType> | undefined = undefined,
PluginFuncArr extends FunctionRet<JimpPlugin> | undefined = undefined,
J extends Jimp = Jimp
J extends JimpConstructors = JimpConstructors
>(
configuration: {
types?: TypesFuncArr;
Expand Down
Loading

0 comments on commit e1d05da

Please sign in to comment.