Skip to content

Commit 2a4b068

Browse files
committed
stream: proper instanceof for Writables
Use `[Symbol.hasInstance]()` to return `true` when asking for `new Duplex() instanceof Writable`. PR-URL: #8834 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Calvin Metcalf <[email protected]>
1 parent a6e1be4 commit 2a4b068

File tree

3 files changed

+57
-4
lines changed

3 files changed

+57
-4
lines changed

doc/api/stream.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -1559,7 +1559,9 @@ Because JavaScript does not have support for multiple inheritance, the
15591559
to extending the `stream.Readable` *and* `stream.Writable` classes).
15601560

15611561
*Note*: The `stream.Duplex` class prototypically inherits from `stream.Readable`
1562-
and parasitically from `stream.Writable`.
1562+
and parasitically from `stream.Writable`, but `instanceof` will work properly
1563+
for both base classes due to overriding [`Symbol.hasInstance`][]
1564+
on `stream.Writable`.
15631565

15641566
Custom Duplex streams *must* call the `new stream.Duplex([options])`
15651567
constructor and implement *both* the `readable._read()` and
@@ -2009,3 +2011,4 @@ readable buffer so there is nothing for a user to consume.
20092011
[Transform]: #stream_class_stream_transform
20102012
[Writable]: #stream_class_stream_writable
20112013
[zlib]: zlib.html
2014+
[`Symbol.hasInstance`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance

lib/_stream_writable.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,32 @@ Object.defineProperty(WritableState.prototype, 'buffer', {
134134
'instead.')
135135
});
136136

137+
// Test _writableState for inheritance to account for Duplex streams,
138+
// whose prototype chain only points to Readable.
139+
var realHasInstance;
140+
if (typeof Symbol === 'function' && Symbol.hasInstance) {
141+
realHasInstance = Function.prototype[Symbol.hasInstance];
142+
Object.defineProperty(Writable, Symbol.hasInstance, {
143+
value: function(object) {
144+
// Trying to move the `realHasInstance` from the Writable constructor
145+
// to here will break the Node.js LazyTransform implementation.
146+
return object._writableState instanceof WritableState;
147+
}
148+
});
149+
} else {
150+
realHasInstance = function(object) {
151+
return object instanceof this;
152+
};
153+
}
154+
137155
function Writable(options) {
138-
// Writable ctor is applied to Duplexes, though they're not
139-
// instanceof Writable, they're instanceof Readable.
140-
if (!(this instanceof Writable) && !(this instanceof Stream.Duplex))
156+
// Writable ctor is applied to Duplexes, too.
157+
// `realHasInstance` is necessary because using plain `instanceof`
158+
// would return false, as no `_writableState` property is attached.
159+
if (!(realHasInstance.call(Writable, this)) &&
160+
!(this instanceof Stream.Duplex)) {
141161
return new Writable(options);
162+
}
142163

143164
this._writableState = new WritableState(options, this);
144165

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
const { Readable, Writable, Duplex, Transform } = require('stream');
5+
6+
const readable = new Readable({ read() {} });
7+
const writable = new Writable({ write() {} });
8+
const duplex = new Duplex({ read() {}, write() {} });
9+
const transform = new Transform({ transform() {} });
10+
11+
assert.ok(readable instanceof Readable);
12+
assert.ok(!(writable instanceof Readable));
13+
assert.ok(duplex instanceof Readable);
14+
assert.ok(transform instanceof Readable);
15+
16+
assert.ok(!(readable instanceof Writable));
17+
assert.ok(writable instanceof Writable);
18+
assert.ok(duplex instanceof Writable);
19+
assert.ok(transform instanceof Writable);
20+
21+
assert.ok(!(readable instanceof Duplex));
22+
assert.ok(!(writable instanceof Duplex));
23+
assert.ok(duplex instanceof Duplex);
24+
assert.ok(transform instanceof Duplex);
25+
26+
assert.ok(!(readable instanceof Transform));
27+
assert.ok(!(writable instanceof Transform));
28+
assert.ok(!(duplex instanceof Transform));
29+
assert.ok(transform instanceof Transform);

0 commit comments

Comments
 (0)