Repository: sindresorhus/got (ts)

Issue:
Fix Electron usage
#### Describe the bug

 - Node.js version: 12.13.0
 - Electron version : 7.0.0 
 - OS & version: linux mint 19.2

When using got in electron, useElectronNet option is not working with electron 7 for POST requests, returns `net::ERR_INVALID_ARGUMENT` error.

#### Actual behavior

HTTP request returns the following error : 
```
GotError [RequestError]: net::ERR_INVALID_ARGUMENT
      at ClientRequest.<anonymous> (/home/vincent/lls/lls-desktop/node_modules/got/source/request-as-event-emitter.js:192:19)
      at Object.onceWrapper (events.js:291:20)
      at ClientRequest.emit (events.js:208:15)
      at ClientRequest.origin.emit (/home/vincent/lls/lls-desktop/node_modules/@szmarczak/http-timer/source/index.js:37:11)
      at URLRequest../lib/browser/api/net.js.URLRequest._emitRequestEvent (electron/js2c/browser_init.js:2284:24) {
    name: 'RequestError',
    host: 'jsonplaceholder.typicode.com',
    hostname: 'jsonplaceholder.typicode.com',
    method: 'POST',
    path: '/posts',
    socketPath: undefined,
    protocol: 'https:',
    url: 'https://jsonplaceholder.typicode.com/posts',
    gotOptions: {
      path: '/posts',
      protocol: 'https:',
      slashes: true,
      auth: null,
      host: 'jsonplaceholder.typicode.com',
      port: null,
      hostname: 'jsonplaceholder.typicode.com',
      hash: null,
      search: null,
      query: null,
      pathname: '/posts',
      href: 'https://jsonplaceholder.typicode.com/posts',
      retry: {
        retries: [Function],
        methods: [Set],
        statusCodes: [Set],
        errorCodes: [Set]
      },
      headers: {
        'user-agent': 'got/9.6.0 (https://github.com/sindresorhus/got)',
        accept: 'application/json',
        'accept-encoding': 'gzip, deflate',
        'content-type': 'application/json',
        'content-length': 39
      },
      hooks: {
        beforeRequest: [],
        beforeRedirect: [],
        beforeRetry: [],
        afterResponse: [],
        beforeError: [],
        init: []
      },
      decompress: true,
      throwHttpErrors: true,
      followRedirect: true,
      stream: false,
      form: false,
      json: true,
      cache: false,
      useElectronNet: true,
      body: '{"title":"foo","body":"bar","userId":1}',
      method: 'POST'
    }
  }

```

#### Expected behavior

Correct HTTP response

#### Code to reproduce

In electron main or renderer process : 

```js
got
    .post('https://jsonplaceholder.typicode.com/posts', {
      body: {
        title: 'foo',
        body: 'bar',
        userId: 1
      },
      json: true,
      useElectronNet: true
    })
    .then(console.log)
    .catch(console.log)
```


`Error: Premature close` on DELETE requests after upgrading to 10.0.3 (from 9.6.0)
#### Describe the bug

We updated from 9.6.0 to 10.0.3 and as soon as it was deployed, we started getting `Error: Premature close` on most or all of our `DELETE` requests. The `GET` and `POST` requests work fine.

Maybe some cases were missed in 7b2ccb0d69fca9aba27373ab344f97d389d692f2?

 - Node.js version: v12.13.1

#### Actual behavior

```
Error: Premature close
  File "internal/streams/end-of-stream.js", line 61, col 15, in IncomingMessage.onclose
  File "events.js", line 210, col 5, in IncomingMessage.emit
  File "domain.js", line 498, col 23, in IncomingMessage.EventEmitter.emit
  File "/home/node/guardian-website/node_modules/@szmarczak/http-timer/dist/index.js", line 40, col 20, in IncomingMessage.origin.emit
    return emit(event, ...args);
  File "_http_client.js", line 363, col 11, in TLSSocket.socketCloseListener
  File "events.js", line 215, col 7, in TLSSocket.emit
  File "domain.js", line 498, col 23, in TLSSocket.EventEmitter.emit
  File "net.js", line 659, col 12, in null.<anonymous>
  File "_tls_wrap.js", line 481, col 7, in TCP.done
```

#### Expected behavior

We shouldn't get a `Premature close` error on successful `DELETE` requests.

#### Code to reproduce

```js
  const url = "https://example.com";
  const username = "username";
  const password = "password";
  const body = undefined;

  const resp = await got(url, {
    username,
    password,
    method: "DELETE",
    throwHttpErrors: false,
    responseType: "json",
    json: body ? body : undefined,
  });
```

#### Checklist

- [X] I have read the documentation.
- [X] I have tried my code with the latest version of Node.js and Got.


Pull request:
Deprecate Electron support
Fixes #899 
Fixes #990

#### Checklist

- [x] I have read the documentation.
- [x] I have included a pull request description of my changes.
- [ ] I have included some tests. (cannot run `ava` + `electron` yet)
- [x] If it's a new feature, I have included documentation updates.


Patch:
diff --git a/readme.md b/readme.md
index 1bbad0f..1b0340a 100644
--- a/readme.md
+++ b/readme.md
@@ -1410,46 +1410,51 @@ const h2got = got.extend({request});
 
 ## FAQ
 
-### Why was Got created?
+### Why yet another HTTP client?
 
-Got was created because the popular [`request`](https://github.com/request/request) package is bloated: [![Install size](https://packagephobia.now.sh/badge?p=request)](https://packagephobia.now.sh/result?p=request)
+Got was created because the popular [`request`](https://github.com/request/request) package is bloated: [![Install size](https://packagephobia.now.sh/badge?p=request)](https://packagephobia.now.sh/result?p=request)\
+Furthermore, Got is fully written in TypeScript.
+
+### Electron support has been deprecated
+
+Some of the Got features may not work properly. See [#899](https://github.com/sindresorhus/got/issues/899) for more info.
 
 ## Comparison
 
-|                       |       `got`      | [`request`][r0] |  [`node-fetch`][n0]  |    [`ky`][k0]     |  [`axios`][a0]   |  [`superagent`][s0]  |
-|-----------------------|:----------------:|:---------------:|:--------------------:|:-----------------:|:----------------:|:--------------------:|
-| HTTP/2 support        |        ❔        |        ❌       |          ❌         |         ❌        |        ❌       |          ✔️\*\*      |
-| Browser support       |        ❌       |        ❌       |          ✔️\*       |         ✔️        |        ✔️       |          ✔️          |
-| Electron support      |        ✔️       |        ❌       |          ❌         |         ❌        |        ❌       |          ❌          |
-| Promise API           |        ✔️       |        ✔️       |          ✔️         |         ✔️        |        ✔️       |          ✔️          |
-| Stream API            |        ✔️       |        ✔️       |     Node.js only     |         ❌        |        ❌       |          ✔️          |
-| Request cancelation   |        ✔️       |        ❌       |          ✔️         |         ✔️        |        ✔️       |          ✔️          |
-| RFC compliant caching |        ✔️       |        ❌       |          ❌         |         ❌        |        ❌       |          ❌          |
-| Cookies (out-of-box)  |        ✔️       |        ✔️       |          ❌         |         ❌        |        ❌       |          ❌          |
-| Follows redirects     |        ✔️       |        ✔️       |          ✔️         |         ✔️        |        ✔️       |          ✔️          |
-| Retries on failure    |        ✔️       |        ❌       |          ❌         |         ✔️        |        ❌       |          ✔️          |
-| Progress events       |        ✔️       |        ❌       |          ❌         |         ✔️\*\*\*  |   Browser only   |          ✔️          |
-| Handles gzip/deflate  |        ✔️       |        ✔️       |          ✔️         |         ✔️        |        ✔️       |          ✔️          |
-| Advanced timeouts     |        ✔️       |        ❌       |          ❌         |         ❌        |        ❌       |          ❌          |
-| Timings               |        ✔️       |        ✔️       |          ❌         |         ❌        |        ❌       |          ❌          |
-| Errors with metadata  |        ✔️       |        ❌       |          ❌         |         ✔️        |        ✔️       |          ❌          |
-| JSON mode             |        ✔️       |        ✔️       |          ✔️         |         ✔️        |        ✔️       |          ✔️          |
-| Custom defaults       |        ✔️       |        ✔️       |          ❌         |         ✔️        |        ✔️       |          ❌          |
-| Composable            |        ✔️       |        ❌       |          ❌         |         ❌        |        ❌       |          ✔️          |
-| Hooks                 |        ✔️       |        ❌       |          ❌         |         ✔️        |        ✔️       |          ❌          |
-| Issues open           |  [![][gio]][g1]  | [![][rio]][r1]  |    [![][nio]][n1]    |   [![][kio]][k1]  |  [![][aio]][a1] |    [![][sio]][s1]     |
-| Issues closed         |  [![][gic]][g2]  | [![][ric]][r2]  |    [![][nic]][n2]    |   [![][kic]][k2]  |  [![][aic]][a2] |    [![][sic]][s2]     |
-| Downloads             |  [![][gd]][g3]   |  [![][rd]][r3]  |    [![][nd]][n3]     |   [![][kd]][k3]   |  [![][ad]][a3]  |    [![][sd]][s3]      |
-| Coverage              |  [![][gc]][g4]   |  [![][rc]][r4]  |    [![][nc]][n4]     |   [![][kc]][k4]   |  [![][ac]][a4]  |    [![][sc]][s4]      |
-| Build                 |  [![][gb]][g5]   |  [![][rb]][r5]  |    [![][nb]][n5]     |   [![][kb]][k5]   |  [![][ab]][a5]  |    [![][sb]][s5]      |
-| Bugs                  |  [![][gbg]][g6]  | [![][rbg]][r6]  |    [![][nbg]][n6]    |   [![][kbg]][k6]  |  [![][abg]][a6] |    [![][sbg]][s6]     |
-| Dependents            |  [![][gdp]][g7]  | [![][rdp]][r7]  |    [![][ndp]][n7]    |   [![][kdp]][k7]  |  [![][adp]][a7] |    [![][sdp]][s7]     |
-| Install size          |  [![][gis]][g8]  | [![][ris]][r8]  |    [![][nis]][n8]    |   [![][kis]][k8]  |  [![][ais]][a8] |    [![][sis]][s8]     |
+|                       | `got`              | [`request`][r0]    | [`node-fetch`][n0]   | [`ky`][k0]               | [`axios`][a0]      | [`superagent`][s0]     |
+|-----------------------|:------------------:|:------------------:|:--------------------:|:------------------------:|:------------------:|:----------------------:|
+| HTTP/2 support        | :grey_question:    | :x:                | :x:                  | :x:                      | :x:                | :heavy_check_mark:\*\* |
+| Browser support       | :x:                | :x:                | :heavy_check_mark:\* | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
+| Electron support      | :x:                | :x:                | :x:                  | :x:                      | :x:                | :x:                    |
+| Promise API           | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
+| Stream API            | :heavy_check_mark: | :heavy_check_mark: | Node.js only         | :x:                      | :x:                | :heavy_check_mark:     |
+| Request cancelation   | :heavy_check_mark: | :x:                | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
+| RFC compliant caching | :heavy_check_mark: | :x:                | :x:                  | :x:                      | :x:                | :x:                    |
+| Cookies (out-of-box)  | :heavy_check_mark: | :heavy_check_mark: | :x:                  | :x:                      | :x:                | :x:                    |
+| Follows redirects     | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
+| Retries on failure    | :heavy_check_mark: | :x:                | :x:                  | :heavy_check_mark:       | :x:                | :heavy_check_mark:     |
+| Progress events       | :heavy_check_mark: | :x:                | :x:                  | :heavy_check_mark:\*\*\* | Browser only       | :heavy_check_mark:     |
+| Handles gzip/deflate  | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
+| Advanced timeouts     | :heavy_check_mark: | :x:                | :x:                  | :x:                      | :x:                | :x:                    |
+| Timings               | :heavy_check_mark: | :heavy_check_mark: | :x:                  | :x:                      | :x:                | :x:                    |
+| Errors with metadata  | :heavy_check_mark: | :x:                | :x:                  | :heavy_check_mark:       | :heavy_check_mark: | :x:                    |
+| JSON mode             | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark:   | :heavy_check_mark:       | :heavy_check_mark: | :heavy_check_mark:     |
+| Custom defaults       | :heavy_check_mark: | :heavy_check_mark: | :x:                  | :heavy_check_mark:       | :heavy_check_mark: | :x:                    |
+| Composable            | :heavy_check_mark: | :x:                | :x:                  | :x:                      | :x:                | :heavy_check_mark:     |
+| Hooks                 | :heavy_check_mark: | :x:                | :x:                  | :heavy_check_mark:       | :heavy_check_mark: | :x:                    |
+| Issues open           | [![][gio]][g1]     | [![][rio]][r1]     | [![][nio]][n1]       | [![][kio]][k1]           | [![][aio]][a1]     | [![][sio]][s1]         |
+| Issues closed         | [![][gic]][g2]     | [![][ric]][r2]     | [![][nic]][n2]       | [![][kic]][k2]           | [![][aic]][a2]     | [![][sic]][s2]         |
+| Downloads             | [![][gd]][g3]      | [![][rd]][r3]      | [![][nd]][n3]        | [![][kd]][k3]            | [![][ad]][a3]      | [![][sd]][s3]          |
+| Coverage              | [![][gc]][g4]      | [![][rc]][r4]      | [![][nc]][n4]        | [![][kc]][k4]            | [![][ac]][a4]      | [![][sc]][s4]          |
+| Build                 | [![][gb]][g5]      | [![][rb]][r5]      | [![][nb]][n5]        | [![][kb]][k5]            | [![][ab]][a5]      | [![][sb]][s5]          |
+| Bugs                  | [![][gbg]][g6]     | [![][rbg]][r6]     | [![][nbg]][n6]       | [![][kbg]][k6]           | [![][abg]][a6]     | [![][sbg]][s6]         |
+| Dependents            | [![][gdp]][g7]     | [![][rdp]][r7]     | [![][ndp]][n7]       | [![][kdp]][k7]           | [![][adp]][a7]     | [![][sdp]][s7]         |
+| Install size          | [![][gis]][g8]     | [![][ris]][r8]     | [![][nis]][n8]       | [![][kis]][k8]           | [![][ais]][a8]     | [![][sis]][s8]         |
 
 \* It's almost API compatible with the browser `fetch` API.\
-\*\* Need to switch the protocol manually.\
-\*\*\* Currently, only 'DownloadProgress' event is supported, 'UploadProgress' event is not supported.\
-❔ Experimental support.
+\*\* Need to switch the protocol manually. Doesn't accept PUSH streams and doesn't reuse HTTP/2 sessions.\
+\*\*\* Currently, only `DownloadProgress` event is supported, `UploadProgress` event is not supported.\
+:grey_question: Experimental support.
 
 <!-- GITHUB -->
 [k0]: https://github.com/sindresorhus/ky
diff --git a/source/as-promise.ts b/source/as-promise.ts
index e838db4..67b7bb6 100644
--- a/source/as-promise.ts
+++ b/source/as-promise.ts
@@ -5,7 +5,7 @@ import is from '@sindresorhus/is';
 import {ParseError, ReadError, HTTPError} from './errors';
 import {normalizeArguments, mergeOptions} from './normalize-arguments';
 import requestAsEventEmitter, {proxyEvents} from './request-as-event-emitter';
-import {CancelableRequest, GeneralError, NormalizedOptions, Response} from './utils/types';
+import {CancelableRequest, GeneralError, NormalizedOptions, Response} from './types';
 
 const parseBody = (body: Buffer, responseType: NormalizedOptions['responseType'], encoding: NormalizedOptions['encoding']): unknown => {
 	if (responseType === 'json') {
@@ -27,7 +27,6 @@ export default function asPromise<T>(options: NormalizedOptions): CancelableRequ
 	const proxy = new EventEmitter();
 	let body: Buffer;
 
-	// @ts-ignore `.json()`, `.buffer()` and `.text()` are added later
 	const promise = new PCancelable<Response | Response['body']>((resolve, reject, onCancel) => {
 		const emitter = requestAsEventEmitter(options);
 		onCancel(emitter.abort);
@@ -84,10 +83,10 @@ export default function asPromise<T>(options: NormalizedOptions): CancelableRequ
 
 			try {
 				for (const [index, hook] of options.hooks.afterResponse.entries()) {
-					// @ts-ignore Promise is not assignable to CancelableRequest
+					// @ts-ignore TS doesn't notice that CancelableRequest is a Promise
 					// eslint-disable-next-line no-await-in-loop
-					response = await hook(response, async (updatedOptions: NormalizedOptions) => {
-						updatedOptions = normalizeArguments(mergeOptions(options, {
+					response = await hook(response, async (updatedOptions): CancelableRequest<Response> => {
+						const typedOptions = normalizeArguments(mergeOptions(options, {
 							...updatedOptions,
 							retry: {
 								calculateDelay: () => 0
@@ -98,21 +97,21 @@ export default function asPromise<T>(options: NormalizedOptions): CancelableRequ
 
 						// Remove any further hooks for that request, because we'll call them anyway.
 						// The loop continues. We don't want duplicates (asPromise recursion).
-						updatedOptions.hooks.afterResponse = options.hooks.afterResponse.slice(0, index);
+						typedOptions.hooks.afterResponse = options.hooks.afterResponse.slice(0, index);
 
 						for (const hook of options.hooks.beforeRetry) {
 							// eslint-disable-next-line no-await-in-loop
-							await hook(updatedOptions);
+							await hook(typedOptions);
 						}
 
-						const promise = asPromise(updatedOptions);
+						const promise = asPromise(typedOptions);
 
 						onCancel(() => {
 							promise.catch(() => {});
 							promise.cancel();
 						});
 
-						return promise;
+						return promise as unknown as CancelableRequest<Response>;
 					});
 				}
 			} catch (error) {
diff --git a/source/as-stream.ts b/source/as-stream.ts
index 7a818d8..3c17306 100644
--- a/source/as-stream.ts
+++ b/source/as-stream.ts
@@ -4,7 +4,7 @@ import {IncomingMessage, ServerResponse} from 'http';
 import {Duplex as DuplexStream, PassThrough as PassThroughStream} from 'stream';
 import {HTTPError, ReadError} from './errors';
 import requestAsEventEmitter, {proxyEvents} from './request-as-event-emitter';
-import {GeneralError, GotEvents, NormalizedOptions, Response} from './utils/types';
+import {GeneralError, GotEvents, NormalizedOptions, Response} from './types';
 
 export class ProxyStream<T = unknown> extends DuplexStream implements GotEvents<ProxyStream<T>> {
 	isFromCache?: boolean;
diff --git a/source/calculate-retry-delay.ts b/source/calculate-retry-delay.ts
index 42ec269..0dcb653 100644
--- a/source/calculate-retry-delay.ts
+++ b/source/calculate-retry-delay.ts
@@ -1,6 +1,6 @@
 import is from '@sindresorhus/is';
 import {HTTPError, ParseError, MaxRedirectsError} from './errors';
-import {RetryFunction, RetryObject} from './utils/types';
+import {RetryFunction, RetryObject} from './types';
 
 const retryAfterStatusCodes: ReadonlySet<number> = new Set([413, 429, 503]);
 
diff --git a/source/create.ts b/source/create.ts
index d2ce534..5d9966f 100644
--- a/source/create.ts
+++ b/source/create.ts
@@ -14,7 +14,7 @@ import {
 	Options,
 	Response,
 	URLOrOptions
-} from './utils/types';
+} from './types';
 
 export type HTTPAlias =
 	| 'get'
@@ -90,7 +90,6 @@ export const defaultHandler: HandlerFunction = (options, next) => next(options);
 
 const create = (defaults: Defaults): Got => {
 	// Proxy properties from next handlers
-	// @ts-ignore Internal use only.
 	defaults._rawHandlers = defaults.handlers;
 	defaults.handlers = defaults.handlers.map(fn => ((options, next) => {
 		// This will be assigned by assigning result
@@ -115,7 +114,6 @@ const create = (defaults: Defaults): Got => {
 		const iterateHandlers = (newOptions: NormalizedOptions): GotReturn => {
 			return defaults.handlers[iteration++](
 				newOptions,
-				// @ts-ignore TS doesn't know that it calls `getPromiseOrStream` at the end
 				iteration === defaults.handlers.length ? getPromiseOrStream : iterateHandlers
 			) as GotReturn;
 		};
@@ -136,15 +134,13 @@ const create = (defaults: Defaults): Got => {
 
 	got.extend = (...instancesOrOptions) => {
 		const optionsArray: Options[] = [defaults.options];
-		// @ts-ignore Internal use only.
-		let handlers: HandlerFunction[] = [...defaults._rawHandlers];
+		let handlers: HandlerFunction[] = [...defaults._rawHandlers!];
 		let mutableDefaults: boolean | undefined;
 
 		for (const value of instancesOrOptions) {
 			if (isGotInstance(value)) {
 				optionsArray.push(value.defaults.options);
-				// @ts-ignore Internal use only.
-				handlers.push(...value.defaults._rawHandlers);
+				handlers.push(...value.defaults._rawHandlers!);
 				mutableDefaults = value.defaults.mutableDefaults;
 			} else {
 				optionsArray.push(value);
@@ -174,7 +170,7 @@ const create = (defaults: Defaults): Got => {
 	got.stream = (url, options) => got(url, {...options, isStream: true});
 
 	for (const method of aliases) {
-		// @ts-ignore GotReturn<Response> does not equal GotReturn<T>
+		// @ts-ignore Cannot properly type a function with multiple definitions yet
 		got[method] = (url: URLOrOptions, options?: Options): GotReturn => got(url, {...options, method});
 		got.stream[method] = (url, options) => got.stream(url, {...options, method});
 	}
diff --git a/source/errors.ts b/source/errors.ts
index ae6c24d..ec62394 100644
--- a/source/errors.ts
+++ b/source/errors.ts
@@ -1,14 +1,14 @@
 import is from '@sindresorhus/is';
 import {Timings} from '@szmarczak/http-timer';
 import {TimeoutError as TimedOutError} from './utils/timed-out';
-import {ErrorCode, Response, NormalizedOptions} from './utils/types';
+import {Response, NormalizedOptions} from './types';
 
 export class GotError extends Error {
-	code?: ErrorCode;
+	code?: string;
 	stack!: string;
 	declare readonly options: NormalizedOptions;
 
-	constructor(message: string, error: Partial<Error & {code?: ErrorCode}>, options: NormalizedOptions) {
+	constructor(message: string, error: Partial<Error & {code?: string}>, options: NormalizedOptions) {
 		super(message);
 		Error.captureStackTrace(this, this.constructor);
 		this.name = 'GotError';
diff --git a/source/get-response.ts b/source/get-response.ts
index 89d8b8f..bc1ceb3 100644
--- a/source/get-response.ts
+++ b/source/get-response.ts
@@ -5,7 +5,7 @@ import stream = require('stream');
 import {IncomingMessage} from 'http';
 import {promisify} from 'util';
 import {createProgressStream} from './progress';
-import {NormalizedOptions} from './utils/types';
+import {NormalizedOptions} from './types';
 
 const pipeline = promisify(stream.pipeline);
 
diff --git a/source/index.ts b/source/index.ts
index f7cfa2b..2d283c8 100644
--- a/source/index.ts
+++ b/source/index.ts
@@ -1,5 +1,5 @@
 import create, {defaultHandler} from './create';
-import {Defaults} from './utils/types';
+import {Defaults} from './types';
 
 const defaults: Defaults = {
 	options: {
@@ -79,7 +79,7 @@ module.exports = got;
 module.exports.default = got;
 
 // Export types
-export * from './utils/types';
+export * from './types';
 
 export {
 	Got,
diff --git a/source/known-hook-events.ts b/source/known-hook-events.ts
index d9cceec..b20d824 100644
--- a/source/known-hook-events.ts
+++ b/source/known-hook-events.ts
@@ -1,4 +1,4 @@
-import {CancelableRequest, GeneralError, NormalizedOptions, Options, Response} from './utils/types';
+import {CancelableRequest, GeneralError, NormalizedOptions, Options, Response} from './types';
 
 /**
 Called with plain request options, right before their normalization. This is especially useful in conjunction with `got.extend()` when the input needs custom handling.
diff --git a/source/normalize-arguments.ts b/source/normalize-arguments.ts
index 5e98d7a..39750ef 100644
--- a/source/normalize-arguments.ts
+++ b/source/normalize-arguments.ts
@@ -1,5 +1,5 @@
 import {URL, URLSearchParams} from 'url';
-import {promisify} from 'util';
+import {promisify, deprecate} from 'util';
 import CacheableRequest = require('cacheable-request');
 import http = require('http');
 import https = require('https');
@@ -26,7 +26,7 @@ import {
 	Options,
 	RequestFunction,
 	URLOrOptions
-} from './utils/types';
+} from './types';
 
 // `preNormalizeArguments` normalizes these options: `headers`, `prefixUrl`, `hooks`, `timeout`, `retry` and `method`.
 // `normalizeArguments` is *only* called on `got(...)`. It normalizes the URL and performs `mergeOptions(...)`.
@@ -132,7 +132,6 @@ export const preNormalizeArguments = (options: Options, defaults?: NormalizedOpt
 	}
 
 	if (options.retry.maxRetryAfter === undefined) {
-		// @ts-ignore We assign if it is undefined, so this IS correct
 		options.retry.maxRetryAfter = Math.min(
 			...[options.timeout.request, options.timeout.connect].filter((n): n is number => !is.nullOrUndefined(n))
 		);
@@ -157,7 +156,7 @@ export const preNormalizeArguments = (options: Options, defaults?: NormalizedOpt
 	// Better memory management, so we don't have to generate a new object every time
 	if (options.cache) {
 		(options as NormalizedOptions).cacheableRequest = new CacheableRequest(
-			// @ts-ignore Types broke on infer
+			// @ts-ignore Cannot properly type a function with multiple definitions yet
 			(requestOptions, handler) => requestOptions.request(requestOptions, handler),
 			options.cache
 		);
@@ -170,7 +169,7 @@ export const preNormalizeArguments = (options: Options, defaults?: NormalizedOpt
 		// Horrible `tough-cookie` check
 		if (setCookie.length === 4 && getCookieString.length === 0) {
 			if (!Reflect.has(setCookie, promisify.custom)) {
-				// @ts-ignore We check for non-promisified setCookie, so this IS correct
+				// @ts-ignore TS is dumb.
 				setCookie = promisify(setCookie.bind(options.cookieJar));
 				getCookieString = promisify(getCookieString.bind(options.cookieJar));
 			}
@@ -436,7 +435,12 @@ export const normalizeRequestArguments = async (options: NormalizedOptions): Pro
 	// `process.version.electron` is used only once, right here.
 	if (options.useElectronNet && (process.versions as any).electron) {
 		const electron = dynamicRequire(module, 'electron') as any; // Trick webpack
-		options.request = electron.net.request ?? electron.remote.net.request;
+		options.request = deprecate(
+			electron.net.request ?? electron.remote.net.request,
+			'Electron support has been deprecated and will be removed in Got 11.\n' +
+			'See https://github.com/sindresorhus/got/issues/899 for further information.',
+			'GOT_ELECTRON'
+		);
 	}
 
 	// Got's `timeout` is an object, http's `timeout` is a number, so they're not compatible.
diff --git a/source/request-as-event-emitter.ts b/source/request-as-event-emitter.ts
index 11e1aee..66a3ded 100644
--- a/source/request-as-event-emitter.ts
+++ b/source/request-as-event-emitter.ts
@@ -13,7 +13,7 @@ import getResponse from './get-response';
 import {normalizeRequestArguments} from './normalize-arguments';
 import {createProgressStream} from './progress';
 import timedOut, {TimeoutError as TimedOutTimeoutError} from './utils/timed-out';
-import {GeneralError, NormalizedOptions, Response} from './utils/types';
+import {GeneralError, NormalizedOptions, Response} from './types';
 import urlToOptions from './utils/url-to-options';
 
 const setImmediateAsync = async (): Promise<void> => new Promise(resolve => setImmediate(resolve));
@@ -82,8 +82,7 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
 				delete typedResponse.fromCache;
 
 				if (!typedResponse.isFromCache) {
-					// @ts-ignore Node.js typings haven't been updated yet
-					typedResponse.ip = response.socket.remoteAddress;
+					typedResponse.ip = response.socket.remoteAddress!;
 				}
 
 				const rawCookies = typedResponse.headers['set-cookie'];
@@ -226,7 +225,7 @@ export default (options: NormalizedOptions): RequestAsEventEmitter => {
 				...urlToOptions(options.url)
 			};
 
-			// @ts-ignore ResponseLike missing socket field, should be fixed upstream
+			// @ts-ignore `cacheable-request` has got invalid types
 			const cacheRequest = options.cacheableRequest!(httpOptions, handleResponse);
 
 			cacheRequest.once('error', (error: GeneralError) => {
diff --git a/source/utils/types.ts b/source/types.ts
similarity index 95%
rename from source/utils/types.ts
rename to source/types.ts
index 4f8db81..84ba589 100644
--- a/source/utils/types.ts
+++ b/source/types.ts
@@ -9,10 +9,10 @@ import {Readable as ReadableStream} from 'stream';
 import {Timings} from '@szmarczak/http-timer';
 import CacheableLookup from 'cacheable-lookup';
 import {Except, Merge, Promisable} from 'type-fest';
-import {GotReturn} from '../create';
-import {GotError, HTTPError, MaxRedirectsError, ParseError} from '../errors';
-import {Hooks} from '../known-hook-events';
-import {URLOptions} from './options-to-url';
+import {GotReturn} from './create';
+import {GotError, HTTPError, MaxRedirectsError, ParseError} from './errors';
+import {Hooks} from './known-hook-events';
+import {URLOptions} from './utils/options-to-url';
 
 export type GeneralError = Error | GotError | HTTPError | MaxRedirectsError | ParseError;
 
@@ -34,16 +34,6 @@ export type Method =
 	| 'options'
 	| 'trace';
 
-export type ErrorCode =
-	| 'ETIMEDOUT'
-	| 'ECONNRESET'
-	| 'EADDRINUSE'
-	| 'ECONNREFUSED'
-	| 'EPIPE'
-	| 'ENOTFOUND'
-	| 'ENETUNREACH'
-	| 'EAI_AGAIN';
-
 export type ResponseType = 'json' | 'buffer' | 'text';
 
 export interface Response<BodyType = unknown> extends http.IncomingMessage {
diff --git a/source/utils/get-body-size.ts b/source/utils/get-body-size.ts
index 313ac51..b6570cd 100644
--- a/source/utils/get-body-size.ts
+++ b/source/utils/get-body-size.ts
@@ -1,19 +1,24 @@
 import {ReadStream, stat} from 'fs';
 import {promisify} from 'util';
+import {ClientRequestArgs} from 'http';
 import is from '@sindresorhus/is';
 import isFormData from './is-form-data';
-import {Options} from './types';
+
+interface Options {
+	body?: unknown;
+	headers: ClientRequestArgs['headers'];
+}
 
 const statAsync = promisify(stat);
 
 export default async (options: Options): Promise<number | undefined> => {
-	const {body, headers, isStream} = options;
+	const {body, headers} = options;
 
 	if (headers && 'content-length' in headers) {
 		return Number(headers['content-length']);
 	}
 
-	if (!body && !isStream) {
+	if (!body) {
 		return 0;
 	}
 
diff --git a/source/utils/timed-out.ts b/source/utils/timed-out.ts
index f8625be..55b7b98 100644
--- a/source/utils/timed-out.ts
+++ b/source/utils/timed-out.ts
@@ -1,6 +1,5 @@
 import net = require('net');
 import {ClientRequest, IncomingMessage} from 'http';
-import {Delays, ErrorCode} from './types';
 import unhandler from './unhandle';
 
 const reentry: unique symbol = Symbol('reentry');
@@ -12,6 +11,26 @@ interface TimedOutOptions {
 	protocol?: string;
 }
 
+export interface Delays {
+	lookup?: number;
+	connect?: number;
+	secureConnect?: number;
+	socket?: number;
+	response?: number;
+	send?: number;
+	request?: number;
+}
+
+export type ErrorCode =
+	| 'ETIMEDOUT'
+	| 'ECONNRESET'
+	| 'EADDRINUSE'
+	| 'ECONNREFUSED'
+	| 'EPIPE'
+	| 'ENOTFOUND'
+	| 'ENETUNREACH'
+	| 'EAI_AGAIN';
+
 export class TimeoutError extends Error {
 	code: ErrorCode;
 
diff --git a/source/utils/url-to-options.ts b/source/utils/url-to-options.ts
index 4fc2502..97c0369 100644
--- a/source/utils/url-to-options.ts
+++ b/source/utils/url-to-options.ts
@@ -22,13 +22,13 @@ export default (url: URL | UrlWithStringQuery): LegacyURLOptions => {
 
 	const options: LegacyURLOptions = {
 		protocol: url.protocol,
-		hostname: url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname,
+		hostname: is.string(url.hostname) && url.hostname.startsWith('[') ? url.hostname.slice(1, -1) : url.hostname,
 		host: url.host,
 		hash: url.hash,
 		search: url.search,
 		pathname: url.pathname,
 		href: url.href,
-		path: is.null_(url.search) ? url.pathname : `${url.pathname}${url.search}`
+		path: `${url.pathname || ''}${url.search || ''}`
 	};
 
 	if (is.string(url.port) && url.port.length !== 0) {
@@ -36,7 +36,7 @@ export default (url: URL | UrlWithStringQuery): LegacyURLOptions => {
 	}
 
 	if (url.username || url.password) {
-		options.auth = `${url.username}:${url.password}`;
+		options.auth = `${url.username || ''}:${url.password || ''}`;
 	}
 
 	return options;


Test patch:
diff --git a/test/url-to-options.ts b/test/url-to-options.ts
index 25abe70..c2aaeab 100644
--- a/test/url-to-options.ts
+++ b/test/url-to-options.ts
@@ -119,3 +119,25 @@ test('does not add null port to options', t => {
 
 	t.deepEqual(options, expected);
 });
+
+test('does not throw if there is no hostname', t => {
+	t.notThrows(() => urlToOptions({} as URL));
+});
+
+test('null password', t => {
+	const options = urlToOptions({
+		username: 'foo',
+		password: null
+	} as any);
+
+	t.is(options.auth, 'foo:');
+});
+
+test('null username', t => {
+	const options = urlToOptions({
+		username: null,
+		password: 'bar'
+	} as any);
+
+	t.is(options.auth, ':bar');
+});