Skip to content

Commit

Permalink
feat: improve types with generic extend
Browse files Browse the repository at this point in the history
  • Loading branch information
spence-s committed May 29, 2024
1 parent 4a44fc4 commit 2135345
Showing 1 changed file with 81 additions and 26 deletions.
107 changes: 81 additions & 26 deletions source/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {Buffer} from 'node:buffer';
import type {Spread} from 'type-fest';
import type {CancelableRequest} from './as-promise/types.js';
import type {Response} from './core/response.js';
import type Options from './core/options.js';
Expand Down Expand Up @@ -69,14 +70,8 @@ export type ExtendOptions = {
mutableDefaults?: boolean;
} & OptionsInit;

export type OptionsOfTextResponseBody = Merge<OptionsInit, {isStream?: false; resolveBodyOnly?: false; responseType?: 'text'}>;
// eslint-disable-next-line @typescript-eslint/naming-convention
export type OptionsOfJSONResponseBody = Merge<OptionsInit, {isStream?: false; resolveBodyOnly?: false; responseType?: 'json'}>;
export type OptionsOfBufferResponseBody = Merge<OptionsInit, {isStream?: false; resolveBodyOnly?: false; responseType: 'buffer'}>;
export type OptionsOfUnknownResponseBody = Merge<OptionsInit, {isStream?: false; resolveBodyOnly?: false}>;
export type StrictOptions = Except<OptionsInit, 'isStream' | 'responseType' | 'resolveBodyOnly'>;
export type StreamOptions = Merge<OptionsInit, {isStream?: true}>;
type ResponseBodyOnly = {resolveBodyOnly: true};

export type OptionsWithPagination<T = unknown, R = unknown> = Merge<OptionsInit, {pagination?: PaginationOptions<T, R>}>;

Expand Down Expand Up @@ -142,26 +137,53 @@ export type GotPaginate = {
& (<T, R = unknown>(options?: OptionsWithPagination<T, R>) => Promise<T[]>);
};

export type GotRequestFunction = {
// `asPromise` usage
(url: string | URL, options?: OptionsOfTextResponseBody): CancelableRequest<Response<string>>;
<T>(url: string | URL, options?: OptionsOfJSONResponseBody): CancelableRequest<Response<T>>;
(url: string | URL, options?: OptionsOfBufferResponseBody): CancelableRequest<Response<Buffer>>;
(url: string | URL, options?: OptionsOfUnknownResponseBody): CancelableRequest<Response>;
export type OptionsOfTextResponseBody = Merge<StrictOptions, {isStream?: false; responseType?: 'text'}>;
export type OptionsOfTextResponseBodyOnly = Merge<StrictOptions, {isStream?: false; resolveBodyOnly: true; responseType?: 'text'}>;
export type OptionsOfTextResponseBodyWrapped = Merge<StrictOptions, {isStream?: false; resolveBodyOnly: false; responseType?: 'text'}>;

(options: OptionsOfTextResponseBody): CancelableRequest<Response<string>>;
<T>(options: OptionsOfJSONResponseBody): CancelableRequest<Response<T>>;
(options: OptionsOfBufferResponseBody): CancelableRequest<Response<Buffer>>;
(options: OptionsOfUnknownResponseBody): CancelableRequest<Response>;
export type OptionsOfJSONResponseBody = Merge<StrictOptions, {isStream?: false; responseType?: 'json'}>; // eslint-disable-line @typescript-eslint/naming-convention
export type OptionsOfJSONResponseBodyOnly = Merge<StrictOptions, {isStream?: false; resolveBodyOnly: true; responseType?: 'json'}>; // eslint-disable-line @typescript-eslint/naming-convention
export type OptionsOfJSONResponseBodyWrapped = Merge<StrictOptions, {isStream?: false; resolveBodyOnly: false; responseType?: 'json'}>; // eslint-disable-line @typescript-eslint/naming-convention

// `resolveBodyOnly` usage
(url: string | URL, options?: (Merge<OptionsOfTextResponseBody, ResponseBodyOnly>)): CancelableRequest<string>;
<T>(url: string | URL, options?: (Merge<OptionsOfJSONResponseBody, ResponseBodyOnly>)): CancelableRequest<T>;
(url: string | URL, options?: (Merge<OptionsOfBufferResponseBody, ResponseBodyOnly>)): CancelableRequest<Buffer>;
export type OptionsOfBufferResponseBody = Merge<StrictOptions, {isStream?: false; responseType?: 'buffer'}>;
export type OptionsOfBufferResponseBodyOnly = Merge<StrictOptions, {isStream?: false; resolveBodyOnly: true; responseType?: 'buffer'}>;
export type OptionsOfBufferResponseBodyWrapped = Merge<StrictOptions, {isStream?: false; resolveBodyOnly: false; responseType?: 'buffer'}>;

(options: (Merge<OptionsOfTextResponseBody, ResponseBodyOnly>)): CancelableRequest<string>;
<T>(options: (Merge<OptionsOfJSONResponseBody, ResponseBodyOnly>)): CancelableRequest<T>;
(options: (Merge<OptionsOfBufferResponseBody, ResponseBodyOnly>)): CancelableRequest<Buffer>;
export type OptionsOfUnknownResponseBody = Merge<StrictOptions, {isStream?: false}>;
export type OptionsOfUnknownResponseBodyOnly = Merge<StrictOptions, {isStream?: false; resolveBodyOnly: true}>;
export type OptionsOfUnknownResponseBodyWrapped = Merge<StrictOptions, {isStream?: false; resolveBodyOnly: false}>;

export type GotRequestFunction<U extends ExtendOptions = Record<string, unknown>> = {
// `asPromise` usage
(url: string | URL, options?: OptionsOfTextResponseBody): U['resolveBodyOnly'] extends true ? CancelableRequest<string> : CancelableRequest<Response<string>>;
<T>(url: string | URL, options?: OptionsOfJSONResponseBody): U['resolveBodyOnly'] extends true ? CancelableRequest<T> : CancelableRequest<Response<T>>;
(url: string | URL, options?: OptionsOfBufferResponseBody): U['resolveBodyOnly'] extends true ? CancelableRequest<Buffer> : CancelableRequest<Response<Buffer>>;
(url: string | URL, options?: OptionsOfUnknownResponseBody): U['resolveBodyOnly'] extends true ? CancelableRequest : CancelableRequest<Response>;

(url: string | URL, options?: OptionsOfTextResponseBodyWrapped): CancelableRequest<Response<string>>;
<T>(url: string | URL, options?: OptionsOfJSONResponseBodyWrapped): CancelableRequest<Response<T>>;
(url: string | URL, options?: OptionsOfBufferResponseBodyWrapped): CancelableRequest<Response<Buffer>>;
(url: string | URL, options?: OptionsOfUnknownResponseBodyWrapped): CancelableRequest<Response>;

(url: string | URL, options?: OptionsOfTextResponseBodyOnly): CancelableRequest<string>;
<T>(url: string | URL, options?: OptionsOfJSONResponseBodyOnly): CancelableRequest<T>;
(url: string | URL, options?: OptionsOfBufferResponseBodyOnly): CancelableRequest<Buffer>;
(url: string | URL, options?: OptionsOfUnknownResponseBodyOnly): CancelableRequest;

(options: OptionsOfTextResponseBody): U['resolveBodyOnly'] extends true ? CancelableRequest<string> : CancelableRequest<Response<string>>;
<T>(options: OptionsOfJSONResponseBody): U['resolveBodyOnly'] extends true ? CancelableRequest<T> : CancelableRequest<Response<T>>;
(options: OptionsOfBufferResponseBody): U['resolveBodyOnly'] extends true ? CancelableRequest<Buffer> : CancelableRequest<Response<Buffer>>;
(options: OptionsOfUnknownResponseBody): U['resolveBodyOnly'] extends true ? CancelableRequest : CancelableRequest<Response>;

(options: OptionsOfTextResponseBodyWrapped): CancelableRequest<Response<string>>;
<T>(options: OptionsOfJSONResponseBodyWrapped): CancelableRequest<Response<T>>;
(options: OptionsOfBufferResponseBodyWrapped): CancelableRequest<Response<Buffer>>;
(options: OptionsOfUnknownResponseBodyWrapped): CancelableRequest<Response>;

(options: OptionsOfTextResponseBodyOnly): CancelableRequest<string>;
<T>(options: OptionsOfJSONResponseBodyOnly): CancelableRequest<T>;
(options: OptionsOfBufferResponseBodyOnly): CancelableRequest<Buffer>;
(options: OptionsOfUnknownResponseBodyOnly): CancelableRequest;

// `asStream` usage
(url: string | URL, options?: Merge<OptionsInit, {isStream: true}>): Request;
Expand Down Expand Up @@ -201,7 +223,7 @@ export type GotStream = GotStreamFunction & Record<HTTPAlias, GotStreamFunction>
/**
An instance of `got`.
*/
export type Got = {
export type Got<GotOptions extends ExtendOptions = ExtendOptions> = {
/**
Sets `options.isStream` to `true`.
Expand Down Expand Up @@ -274,5 +296,38 @@ export type Got = {
// x-unicorn: rainbow
```
*/
extend: (...instancesOrOptions: Array<Got | ExtendOptions>) => Got;
} & Record<HTTPAlias, GotRequestFunction> & GotRequestFunction;
extend<T extends Array<Got | ExtendOptions>>(...instancesOrOptions: T): Got<MergeExtendsConfig<T>>;
}
& Record<HTTPAlias, GotRequestFunction<GotOptions>>
& GotRequestFunction<GotOptions>;

/**
* Merges the options of multiple Got instances.
*/
type MergeExtendsConfig<Value extends Array<Got | ExtendOptions>> =
Value extends readonly [Value[0], ...infer NextValue]
? NextValue[0] extends undefined
? Value[0] extends infer OnlyValue
? OnlyValue extends Got<infer GotOptions>
? GotOptions
: OnlyValue
: never
: NextValue[0] extends infer NextArg extends ExtendOptions
? Spread<Value[0], NextArg> extends infer Merged extends ExtendOptions
? NextValue extends [NextValue[0], ...infer NextRest]
? NextRest extends Array<Got | ExtendOptions>
? MergeExtendsConfig<[Merged, ...NextRest]>
: never
: never
: never
: NextValue[0] extends Got<infer NextArg>
? Spread<Value[0], NextArg> extends infer Merged extends ExtendOptions
? NextValue extends [NextValue[0], ...infer NextRest]
? NextRest extends Array<Got | ExtendOptions>
? MergeExtendsConfig<[Merged, ...NextRest]>
: never
: never
: never
: never
: never;

0 comments on commit 2135345

Please sign in to comment.