Skip to content

feat: strict TypeScript configuration #2010

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

SmashingQuasar
Copy link
Contributor

Description

Following the discussion from #2006, this PR aims to implement a strict TypeScript configuration.

@SmashingQuasar
Copy link
Contributor Author

@robertsLando : I already resolved many issues but this is taking some time to fix so I will continue in the upcoming days.

There are a few cases where I elected to put an @ts-expect-error directive to tell TypeScript that it should find an error there but ignore it.
Sometimes, I added an @TODO with some ideas for improvement. I also mentioned you a few times (since I don't know who else can be involved in this). We can remove those @TODO before merging, I just wanted to draw your attention on those.

Copy link
Contributor Author

@SmashingQuasar SmashingQuasar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a few comments just to give more insight on what I did so far.

import { pipeline } from 'stream'

const helpMe = help({
dir: path.join(__dirname, '../../', 'help'),
dir: path.join(import.meta.dirname, '../../', 'help'),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly necessary but enables migration to ESM.

Comment on lines +14 to +15
import publish from './pub.js'
import subscribe from './sub.js'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best practice to avoid confusion between folders and files, there are also other more specific requirements from Node.js and browsers if using .mjs extension.

Comment on lines +126 to +130
parsedArgs['will'] = {}
parsedArgs['will'].topic = parsedArgs['will-topic']
parsedArgs['will'].payload = parsedArgs['will-message']
parsedArgs['will'].qos = parsedArgs['will-qos']
parsedArgs['will'].retain = parsedArgs['will-retain']
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these, and every access made that way can be easily resolved by using typeguards in a later PR.

Comment on lines +5 to +8
interface WritevChunksParameters {
chunk: unknown
encoding: string
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I elected to create this very simple interface to limit cognitive load on function signature. It also simplified later typing.

@@ -62,11 +80,15 @@ export class BufferedDuplex extends Duplex {
})
}

_read(size?: number): void {
public override _read(size?: number): void {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The override keyword is mandatory here to indicate we are overriding the behaviour of a method that was already defined in the parent class. This improves maintainability.

}

private clear() {
if (this.timerId) {
if (this.timerId !== undefined) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly a TypeScript issue, but ESlint will complain if we use a strict configuration, as nullish comparisons can lead to problematic bugs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if timerId is null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timerId cannot be null in this code.

It is declared line 9 as:

	private timerId: number | undefined

It is then used from lines 50 to 55 here:

	private clear() {
		if (this.timerId !== undefined) {
			this.timer.clear(this.timerId)
			this.timerId = undefined
		}
	}

It's last reference is line 101 with:

		this.timerId = this.timer.set(() => {
			// this should never happen, but just in case
			if (this.destroyed) {
				return
			}

			this.counter += 1

			// after keepalive seconds, send a pingreq
			if (this.counter === 2) {
				this.client.sendPing()
			} else if (this.counter > 2) {
				this.client.onKeepaliveTimeout()
			}
		}, this._intervalEvery)

The Timer.prototype.set method can only return a number:
image

This means timerId can only be either a number or undefined.

Comment on lines +94 to 99
// @TODO: this._keepalive may be undefined. This operation will result in NaN.
const keepAliveTimeout = Math.ceil(this._keepalive * 1.5)

this._keepaliveTimeoutTimestamp = Date.now() + keepAliveTimeout
// @TODO: this._keepalive may be undefined. This operation will result in NaN.
this._intervalEvery = Math.ceil(this._keepalive / 2)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A dangerous code that was revealed by using a strict TypeScript configuration. You have two very clear cases where those properties can unexpectedly end up as NaN.

Comment on lines +84 to +93
// @TODO: @robertsLando We need to handle this situation as if data is null, Buffer.from(null) will throw an Error
if (data === null) {
throw new Error('')
}

if (data instanceof ArrayBuffer) {
data = Buffer.from(data)
} else {
data = Buffer.from(data, 'utf8')
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here again a strict TypeScript configuration reveals a potential error that will be uncaught and thus bubble back to our end users code. I left this new Error('') blank for now since I still need to understand better what to do in this situation. I'm not completely familiar with this code base.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not completely familiar with this code base.

The problem with this is I never had a way to test this out, the user using this are very few and sincerely I dunno if it works or not. Same for wx, when I did the TS refactor I just focused on converting it to TS without worry too much about it

Copy link
Member

@robertsLando robertsLando Jul 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a plan BTW to drop them and put them on a separated repo, same for pub/sub bin but giving my limited time I'm just focusing on fixing bugs for now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, every code base has problems, even if you have 100% coverage, 100% stryker score and 0 SonarQube issue you have problems that are not clearly identified. I'm just highlighting the potential benefits of a stricter configuration.
This is actually really positive as we can improve this part since we know it has a potential issue here. 👍

@robertsLando robertsLando changed the title Strict TypeScript configuration feat: strict TypeScript configuration Jul 8, 2025
@@ -132,8 +132,13 @@
"@esm-bundle/chai": "^4.3.4",
"@release-it/conventional-changelog": "^7.0.2",
"@types/chai": "^4.3.20",
"@types/concat-stream": "^2.0.3",
"@types/debug": "^4.1.12",
"@types/minimist": "^1.2.5",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could drop minimis in favor of nodejs parseArgs but maybe we can do that in another PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, just make sure that this does not break browsers implementation. I think there is a global dependency review to be done on the overall code. Let's add this to a roadmap if there is one. 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just make sure that this does not break browsers implementation

Minimist isn't used on browser side but just on /bin files that can only be run from nodejs :)


export default class KeepaliveManager {
// @ts-expect-error - @TODO: @robertsLando: You have a situation where _keepalive can be undefined which will lead to cascading problems due to _keepalive being always used directly as a number.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this throw if so?

@@ -584,7 +575,8 @@ export default class MqttClient extends TypedEventEmitter<MqttClientEventCallbac
options.protocolVersion === 5 && options.customHandleAcks
? options.customHandleAcks
: (...args) => {
args[3](null, 0)
// @TODO: This would really need to be made easier to understand. It is difficult to know why the first parameter is Error | number.
args[3](0, 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if using 0 instead of null here may lead to some errors elsewhere? I would stick to undefined and fix the type here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. This section of the code is treacherous. I think the best course of action would be to refactor it so it is simpler to understand. I am trying to limit the changes I am making to the bare minimum to have a strict TypeScript configuration so the review is easier. I'll see what I can do make this more stable.

@@ -722,6 +718,7 @@ export default class MqttClient extends TypedEventEmitter<MqttClientEventCallbac
* @return the auth packet to be returned to the broker
* @api public
*/
// @ts-expect-error - packet is unused. @TODO: This method is seemingly never called.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never called? It's actually used AFAIK

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, VSCode failed to detect the call within auth.ts.

After having a look, here is the full method declaration:

	/**
	 * @param packet the packet received by the broker
	 * @return the auth packet to be returned to the broker
	 * @api public
	 */
	// @ts-expect-error - packet is unused. @TODO: This method is seemingly never called.
	public handleAuth(packet: IAuthPacket, callback: PacketCallback) {
		callback()
	}

The only place I find it used is here:

	client.handleAuth(
		packet,
		(err: ErrorWithReasonCode, packet2: IAuthPacket) => {
			if (err) {
				client.emit('error', err)
				return
			}

			if (rc === 24) {
				client.reconnecting = false
				client['_sendPacket'](packet2)
			} else {
				const error = new ErrorWithReasonCode(
					`Connection refused: ${ReasonCodes[rc]}`,
					rc,
				)
				client.emit('error', error)
			}
		},
	)

If we look at the code, we can see that both arguments err and packet2 will both be undefined as the handleAuth implementation passes no argument to the callback argument invocation.

From what I can see, the code that is really being ran is:

			if (rc === 24) {
				client.reconnecting = false
				client['_sendPacket'](undefined) // packet2 is undefined so I replaced it by its constant value
			} else {
				const error = new ErrorWithReasonCode(
					`Connection refused: ${ReasonCodes[rc]}`,
					rc,
				)
				client.emit('error', error)
			}

I haven't had a look at auth.ts so I can't tell if this is intended.

@@ -734,6 +731,7 @@ export default class MqttClient extends TypedEventEmitter<MqttClientEventCallbac
* @param Function callback call when finished
* @api public
*/
// @ts-expect-error - packet is unused. @TODO: This method is seemingly never called.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is never used but can be overridden by user in order to handle messages with backpressure on stream (instead of using message event that doesn't provide backpressure)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I don't have the entire code in mind, I'll remove the @TODO here since I can't really tell how an end-user would use this.

Comment on lines +861 to +862
// @ts-expect-error - @TODO: @robertsLando this is a dangerous situation as this.options.password can never be a Buffer but was forced as a Buffer
password: this.options.password,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, there was a PR on mqtt-packet to fix this type don't remember right now if it has been merged or not but it was safe to force it to buffer especially because almost everyone passes it as string

Comment on lines +866 to +868
if (this.options.clientId !== undefined) {
connectPacket.clientId = this.options.clientId
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clientId should never be undefined, we generate a random one in case it is

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MQTT.js/src/lib/client.ts

Lines 576 to 579 in 1720b64

this.options.clientId =
typeof options.clientId === 'string'
? options.clientId
: MqttClient.defaultId()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but in that case, the corresponding interface IClientOptions should reflect that and forbid the property from being undefined.

@@ -1121,15 +1127,15 @@ export default class MqttClient extends TypedEventEmitter<MqttClientEventCallbac
// force re-subscribe on reconnect. This is only true
// when provided `topicObject` is `this._resubscribeTopics`
let resubscribe = false
let topicsList = []
let topicsList: Array<string> = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not string[] ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a habit that I have, it's not even conscious at this point.

There is actually a good reason to this.
In TypeScript, [] can be used to defined two very different but related structures.
string[] is equivalent to Array<string>. This means an array of length 0 or more.
However, you can also use TypeScript tuples, which are specific arrays, for example:
[string, ...Array<string>] which means : "An array of strings with a minimum length of 1".

I've seen many developers quickly get confused when getting this kind of typing (I was even slightly confused while typing it):

const parsedValue: [[string, ...string[]], [string, ...string[]]];

I usually write:

const parsedValue: [[string, ...Array<string>], [string, ...Array<string]];

This helps with readability and isolates [] use to tuples only to reduce cognitive load.
Sub-typed structures also have a common structure in TypeScript:

const bar: Record<string, unknown> = {};

class Foo<T> {
   public setSomething(value: string): void {}
}

const myFoo: Foo<string> = new Foo<string>();

type FooSetSomething = Parameters<Foo<string>['setSomething']>;

const myMap: Map<string, string> = new Map<string, string>();

I find it more consistent to respect this syntax for every structure instead of creating an exception for arrays, especially if you end up with this kind of type:

const multiTypesArray: (number|string|string[])[] = [];

This is just an opinion. If you want to switch to string[] just let me know, it's not a problem.

Comment on lines -1153 to -1156
if (version === 5) {
defaultOpts.nl = false
defaultOpts.rap = false
defaultOpts.rh = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this has been removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code was set here, and then immediately reaffected under the similar condition here:

				if (version === 5) {
					currentOpts.nl = reconciledOptions.nl ?? false
					currentOpts.rap = reconciledOptions.rap ?? false
					currentOpts.rh = reconciledOptions.rh ?? 0
					// use opts.properties
					currentOpts.properties = properties
				}

So I simply moved it in one place. This resolved some typing issues since the interface indicates that nl, rap and rh can be undefined.

Comment on lines +4 to +5
"module": "NodeNext",
"moduleResolution": "NodeNext",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dunno why I didn't do it here but I usually extend node default tsconfig here, like https://www.npmjs.com/package/@tsconfig/node20.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you if you want to use a base, I'm providing the strictest configuration I currently am aware of.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting with the strictest config is not a smart idea imho, it will bring too many changes in a single PR. I'd proceed way more cautious since this library is literally used by millions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the concerns. Then it might be impossible to switch to a strict ESlint configuration because there are hundreds of rules, which would take extremely long to implement rule by rule.

I'll start over with one rule at a time...

@DavideViolante
Copy link
Contributor

DavideViolante commented Jul 8, 2025

I'd proceed more carefully on this and on a step by step basis. Maybe initially just adding "noImplicitAny" rule and merging less changes, then adding stricter properties like strict step by step in different PRs. Also I'd change the "module" in a different PR. Just my thoughts. This lib is used by millions, this PR imho is waaaaay to big.

@SmashingQuasar
Copy link
Contributor Author

I'd proceed more carefully on this and on a step by step basis. Maybe initially just adding strict to tsconfig and merging less changes, then adding stricter properties step by step in different PRs. Also I'd change the "module" in a different PR. Just my thoughts.

We could do it that way if that makes people more comfortable. Let me know how we shall proceed and I will adapt the process accordingly.

@SmashingQuasar
Copy link
Contributor Author

I just pushed a commit with many new issues fixed. I haven't cleaned up the previously discussed one for now, I'll do a complete clean up at the end to avoid having to go back and forth. 225 errors left to resolve.

Copy link
Contributor Author

@SmashingQuasar SmashingQuasar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I resolved many typing issues again and added some comments to help with the review.

Comment on lines +1709 to +1710
this.outgoing[+messageId]?.volatile &&
typeof this.outgoing[+messageId]?.cb === 'function'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit but discreet cast to number.

@@ -1749,17 +1754,19 @@ export default class MqttClient extends TypedEventEmitter<MqttClientEventCallbac

if (topic.length === 0) {
// restore topic from alias
if (typeof alias === 'undefined') {
if (alias === undefined) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The syntax:

typeof x === 'undefined'

Is no longer useful as ECMAScript made the undefined global symbol read-only many years ago. This was a necessity for everything prior ES5.

@@ -1799,6 +1806,7 @@ export default class MqttClient extends TypedEventEmitter<MqttClientEventCallbac
if (
!this.disconnecting &&
!this.reconnectTimer &&
// @ts-expect-error - reconnectPeriod can be undefined, but changing it without prior refactoring may lead to problems.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here, what I think is happening, is that we want to use the same instantiation interface and definition interface, which creates these situations where we constantly have to check if anything is undefined.

Comment on lines +1921 to 1922
// @ts-expect-error - This situation is really problematic. clone() returns a function, not an object, so this code needs to be looked at. Is this used and tested?
storePacket = clone(packet)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very likely that this code is never ran as storePacket ends up being the cloning function and not the cloned packet. clone() returns the cloning function so the correct syntax would be clone()(packet). This is because the developers of this function used a purely functional approach. (which is not inherently bad but requires care when used to avoid this kind of situation)

Copy link
Member

@robertsLando robertsLando Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. The export of default is:

module.exports = require('./index.js')()

So I think this is actually working

*/
private _sendPacket(
public _sendPacket(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is definitely public, as it is called from other files.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for _onConnect... dunno what's best to do

Comment on lines +2165 to +2174
const messageId: number | undefined = storePacket?.messageId

if (
messageId !== undefined &&
this.outgoing[messageId] !== undefined
) {
// @ts-expect-error - cb and this.outgoing[messageId].cb have incompatible types.
cb = this.outgoing[messageId].cb
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think that we should refactor all properties to actually use explicit (and not magic!) getters to guarantee types and avoid the perpetual pain of having possibly undefined values everywhere. It would considerably reduce the size of the code as well.

*/
private _onConnect(packet: IConnackPacket) {
public _onConnect(packet: IConnackPacket) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely public as it is called from other files.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah kind of, in fact we don't really want this to be called by users but we need to call it in our handlers, they used to be inside client.ts but I moved them away as I would like to split it as there was too much code inside that single class

Comment on lines +31 to +32
const results: RegExpExecArray | null =
/^(?<username>.+):(?<password>.+)$/.exec(opts.auth)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RegExp.prototype.exec is the recommended way to use regular expressions nowadays. I also converted this to use named groups to make the intent clear.

Comment on lines +56 to +57
// @robertsLando: This is a dangerous evaluation of brokerUrl as typeof null === 'object' will return true
if (typeof brokerUrl === 'object' && opts === undefined) {
Copy link
Contributor Author

@SmashingQuasar SmashingQuasar Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this example in fact as it clearly highlights why I always ban null in all code base that I work with except for JSON serialisation.
The following code:

console.debug(typeof null === 'object');

Will output true.

This is because null is not actually null. This means that it is indeed an object.
The two programming concepts you usually want to use is null value which exists only in the form:

const myValue: unknown = undefined; // This is a true reference to null value

And null reference, which can be observed that way:

interface ExampleInterface
{
  possibleNullReference?: string;
}

const example1: ExampleInterface = {
  possibleNullReference: "foo"
}

console.debug(Object.hasOwn(example1, "possibleNullReference")); // true

const example2: ExampleInterface = {};

console.debug(Object.hasOwn(example2, "possibleNullReference")); // false

Which is also why those two syntax:

interface ExampleInterface {
  foo?: string
}

and

interface ExampleInterface {
  foo: string | undefined
}

are not equivalent.

Additionally, you can even find even weirder situations such as:

const foo = Object.create(null);

foo.bar = "This property exists";

console.debug(typeof foo); // "object" so Javascript clearly identifies this as an object type
console.debug(foo instanceof Object); // false because this object does not have the Object prototype, it's a void-prototype object
console.debug(foo.bar); // "This property exists" => It is still a functioning object

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep I know that and I agree about having more strict types for this reason

Comment on lines +163 to +166
// @TODO: This is very heavy on the runtime, especially when working with CJS, as you may be living in a cache-busted environment which will result in significant IOPS because of the repeated require()
// Consider either migrating to ESM, which is immutable and thus immune to cache busting, or simply requiring each file once.
// Note: Those situations are very rare, but considering how widespread MQTT is within the IoT world, it might be worth considering
if (protocols === undefined) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, require is actually a rather simple module import implementation based on a capsule.
A CJS module actually is something like:

((__dirname, __filename, module) => {
  // Your CJS code
})()

The problem is that CJS is mutable because it is not built-in Javascript. So if you were to do this:

global.require = (path) => {
  const result = require(path);

  Object.keys(require.cache).forEach((key) => {
    delete require.cache[key]
  })

  return result
}

Then the require cache would cease to exist, which will result in an extreme amount of IOPS when constantly calling require.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

considering that this happens only on client creation I don't think it's a real bottle neck for performances BTW I'm open for improvements :)

Comment on lines +17 to +18
// @TODO: Defaulting localhost has a few minor pitfalls. Notably because localhost triggers DNS resolution which might not map to 127.0.0.1 and create problems.
scopedOpts.hostname = opts?.hostname ?? opts?.host ?? 'localhost'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW using 127.0.0.1 here may cause other compatibility issues I think?

if (rc === 0) {
client.reconnecting = false
client['_onConnect'](packet)
client._onConnect(packet)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see my comment

Comment on lines +42 to +44
if (done !== undefined) {
done()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about assing noop to done when not defined? This would remove some useless code

@robertsLando
Copy link
Member

@SmashingQuasar Giving the growing amount of changes this PR is introducing I agree with @DavideViolante that a better and safer approach could be to split this into smaller PR where in each one you enable 1/2 strict rules of TS till we reach our goal.

@SmashingQuasar
Copy link
Contributor Author

@SmashingQuasar Giving the growing amount of changes this PR is introducing I agree with @DavideViolante that a better and safer approach could be to split this into smaller PR where in each one you enable 1/2 strict rules of TS till we reach our goal.

Alright, though this is getting very painful for me as I will have to start over. I'll do it with the minimal number of rules possible to avoid having to redo the entire work again.

@SmashingQuasar
Copy link
Contributor Author

Closing this PR to reopen it one rule at a time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants