Skip to content

feat: add types#69

Open
Phillip9587 wants to merge 9 commits intojshttp:v3from
Phillip9587:types
Open

feat: add types#69
Phillip9587 wants to merge 9 commits intojshttp:v3from
Phillip9587:types

Conversation

@Phillip9587
Copy link
Contributor

This PR adds type definitions to finalhandler. It includes the following changes:

Ref: expressjs/typescript-wg#1

cc @RobinTail

@socket-security
Copy link

socket-security bot commented Aug 14, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​tsconfig/​node18@​18.2.61001007185100
Added@​types/​node@​18.19.1301001008196100
Added@​arethetypeswrong/​cli@​0.18.210010010083100
Addedexpect-type@​1.3.010010010084100
Addedtypescript@​5.9.31001009010090

View full report

@Phillip9587 Phillip9587 requested a review from jonchurch October 27, 2025 20:41
@Phillip9587
Copy link
Contributor Author

@jonchurch @bjohansebas @clicktodev Could you please take a look at this PR when you get a chance? It’s part of the TypeScript WG initiative, and since we’re members, your input would be valuable.

Copy link
Member

@bjohansebas bjohansebas left a comment

Choose a reason for hiding this comment

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

Actually, I think this should be targeted at version 2, since that’s the one being depended on. These changes can be backported to version 3

@Phillip9587
Copy link
Contributor Author

@bjohansebas I recommend merging this into v3. If it turns out to be necessary, we can backport it to v2 afterwards.

@clicktodev
Copy link

the advantage of releasing this as a new major release is that people will be more likely to read the patch notes since it's a major release and they will discover that they shouldn't install third party types anymore.
the only problem with releasing the types in the existing release is that it may conflict with third party types.

@bjohansebas
Copy link
Member

okay, so let's release it as part of version 3 then

Copy link
Member

@bjohansebas bjohansebas left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Member

@jonchurch jonchurch left a comment

Choose a reason for hiding this comment

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

Had a question about ServerResponse vs OutgoingMessage, which is mostly a nit #69 (comment)

The undefined for error seems off, Im confident it is but not 100%


declare function onFinished<T extends IncomingMessage | ServerResponse>(
msg: T,
listener: (err: Error | null | undefined, msg: T) => void
Copy link
Member

Choose a reason for hiding this comment

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

I don't think undefined is possible here.

Suggested change
listener: (err: Error | null | undefined, msg: T) => void
listener: (err: Error | null, msg: T) => void

Copy link
Member

Choose a reason for hiding this comment

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

It’s a behavior I’m seeing in #87 , maybe there’s a bug? I’m still working on migrating those tests to HTTP/2.

Copy link
Member

Choose a reason for hiding this comment

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

Can you point to a reproduction of the behavior?

Copy link
Member

@bjohansebas bjohansebas Jan 12, 2026

Choose a reason for hiding this comment

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

const http2 = require('http2')
const onFinished = require('..')

var server = http2.createServer(function (req, res) {
        onFinished(res, function (_err, msg) {
          console.log(_err)
          assert.strictEqual(msg, res)
          server.close(done)
        })
      })

      server.listen(function () {
        var port = this.address().port
        var client = http2.connect('http://127.0.0.1:' + port)
        client.on('error', noop)
        client.on('close', noop)

        var req = client.request({ ':path': '/' })
        req.on('response', function () {})
        req.end()

        setImmediate(function () {
          client.destroy()
        })
      })

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I keep not checking v3 branch.

This is why typescript rules lol. It's catching a regression in types (or at least a difference, @Phillip9587 wasundefined intentional as a way to differentiate different close scenarios?)

onFinish()

ee-first always normalized the error arg for us to Error | null:
https://github.com/jonathanong/ee-first/blob/master/index.js#L84-L86

When v3 switched to stream.finished(), that normalization was lost. I think we should fix the v3 code to explicitly pass null here (and on line 128 for socket close), keeping the type as Error | null to match v2 behavior.

At least, unless there is a compelling reason to change the behavior, keeping it the same makes sense to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is undefined a problem here? I updated the types intentionally because it is the actual behaviour after #56. I can update the onFinish function to normalize to null if we want to keep the existing behaviour:

on-finished/index.js

Lines 97 to 101 in 57ba282

function onFinish (error) {
if (finished) return
finished = true
callback(error)
}

But even the stream.finished() callback's error is typed as optional so it includes undefined:

function finished(
    stream: NodeJS.ReadableStream | NodeJS.WritableStream | web.ReadableStream | web.WritableStream,
    callback: (err?: NodeJS.ErrnoException | null) => void,
): () => void;

Source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/bc2d982e44ddd1ef9b27a3e4a3f24da245d23336/types/node/stream.d.ts#L1374-L1377

I would prefer not normalizing to null and calling the callback without a parameter.

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 it’s better to leave it as is, after all, this is just a wrapper around finished(), and this is how Node.js returns it, so we should leave it that way.

Phillip9587 and others added 2 commits January 15, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants