javascript - Prečo operátor `instanceof` vracia false pre inštanciu odovzdanú do knižnice? (Žiadna dedičnosť neprichádza do úvahy)


Staviam knižnicu v TS. Táto knižnica používa knižnicu ssh2 ako závislosť.

Snažím sa vytvoriť funkciu, ktorá môže buď prijať ssh2 konfiguračný objekt alebo už existujúcu inštanciu Client na vykonanie príkazu (toto je zjednodušený prípad):

import { Client, ConnectConfig } from 'ssh2';

export function runCommand(params: Client | ConnectConfig) {
  if(params instanceof Client) {
    // do something
  } else {
    // create our own client
    // do something
  }
}

Keď zostavím túto knižnicu a zavolám funkciu takto:

const { readFileSync } = require("fs");
const { Client } = require("ssh2");
const { runCommand } = require("myLib");
const conn = new Client();

conn
  .on("ready", () => {
    console.log("Client :: ready");
    runCommand(conn);
  })
  .connect({
    host: "example.com",
    port: 22,
    username: "me",
    privateKey: readFileSync(process.env.HOME + "/.ssh/id_ed25519"),
  });

kontrola instanceof v mojej funkcii z nejakého dôvodu vráti false. Čo sa tu presne deje? Je to problém s kompiláciou TS alebo Node.js vidí Client zo závislosti mojej knižnice a zo závislosti môjho klientského kódu ako dve rôzne triedy?

Zostavený kód vyzerá nejako takto:

const ssh2_1 = require("ssh2");
//...
function runCommand(params) {
    if (params instanceof ssh2_1.Client) {
        // do something
    }
    else {
       // create our own client
    // do something
    }
}

Odpoveď č. 1

Čo nemusí byť zjavné z chovania operátora instanceof, je, že trieda musí byť presne v reťazci rodičov, v zmysle JavaScript objektovej referencie.

Preto, ak naimportujete triedu Client dvakrát, ide o rôzne skutočné referencie objektov a instanceof môže dať „false“ negatívny výsledok.

Toto je veľmi pravdepodobne to, čo sa tu deje: ssh2 je zabalený vo vašej knižnici, preto importuje svoju vlastnú kópiu závislosti. Zatiaľ čo vaša aplikácia importuje ssh2 sama, čo vedie k samostatnej kópii.

Máte niekoľko možností:

  • Neposkytujte závislosť spolu s vašou knižnicou (napr. pomocou webpack externals), nech aplikácia nainštaluje jednu verziu, ktorú budú importovať ako vaša knižnica, tak aj aplikácia; typický prípad špecifikovania peerDependencies
  • Nepoužívajte operátor instanceof; namiesto toho použite nejakú heuristiku na určenie, či objekt má tvar, ktorý potrebujete; typicky skontrolujte niektoré vlastnosti a ich typ
  • Re-exportujte závislosť (napr. triedu Client vo vašom prípade) z vašej knižnice a používajte túto vo vašej aplikácii (const { Client, runCommand }=require("myLib");), aby to bola presne tá istá kópia (keďže je už zabudovaná, prečo ju znovu nezabaliť namiesto opätovného balenia?)

Súvisiace články