diff --git a/API.md b/API.md index 512cd80c..1c05813e 100644 --- a/API.md +++ b/API.md @@ -80,10 +80,24 @@ toString(): string __Returns__: * string -#### *static* isConstruct(x)⚠️ +#### *static* isConstruct(x) Checks if `x` is a construct. +Use this method instead of `instanceof` to properly detect `Construct` +instances, even when the construct library is symlinked. + +Explanation: in JavaScript, multiple copies of the `constructs` library on +disk are seen as independent, completely different libraries. As a +consequence, the class `Construct` in each copy of the `constructs` library +is seen as a different class, and an instance of one class will not test as +`instanceof` the other class. `npm install` will not create installations +like this, but users may manually symlink construct libraries together or +use a monorepo tool: in those cases, multiple copies of the `constructs` +library can be accidentally installed, and `instanceof` will behave +unpredictably. It is safest to avoid using `instanceof`, and using +this type-testing method instead. + ```ts static isConstruct(x: any): boolean ``` diff --git a/src/construct.ts b/src/construct.ts index 021d2220..2e7444f2 100644 --- a/src/construct.ts +++ b/src/construct.ts @@ -3,6 +3,8 @@ import { MetadataEntry } from './metadata'; import { captureStackTrace } from './private/stack-trace'; import { addressOf } from './private/uniqueid'; +const CONSTRUCT_SYM = Symbol.for('constructs.Construct'); + /** * Represents a construct. */ @@ -422,13 +424,26 @@ export class Node { export class Construct implements IConstruct { /** * Checks if `x` is a construct. + * + * Use this method instead of `instanceof` to properly detect `Construct` + * instances, even when the construct library is symlinked. + * + * Explanation: in JavaScript, multiple copies of the `constructs` library on + * disk are seen as independent, completely different libraries. As a + * consequence, the class `Construct` in each copy of the `constructs` library + * is seen as a different class, and an instance of one class will not test as + * `instanceof` the other class. `npm install` will not create installations + * like this, but users may manually symlink construct libraries together or + * use a monorepo tool: in those cases, multiple copies of the `constructs` + * library can be accidentally installed, and `instanceof` will behave + * unpredictably. It is safest to avoid using `instanceof`, and using + * this type-testing method instead. + * * @returns true if `x` is an object created from a class which extends `Construct`. * @param x Any object - * - * @deprecated use `x instanceof Construct` instead */ public static isConstruct(x: any): x is Construct { - return x instanceof Construct; + return x && typeof x === 'object' && x[CONSTRUCT_SYM]; } /** @@ -536,3 +551,10 @@ export interface MetadataOptions { */ readonly traceFromFunction?: any; } + +// Mark all instances of 'Construct' +Object.defineProperty(Construct.prototype, CONSTRUCT_SYM, { + value: true, + enumerable: false, + writable: false, +}); \ No newline at end of file