testClassesAreNominal
Classes are nominal: their name is part of the hash.
import { analyzeProject } from "#codebase/tcmts/core/analyze"; import assert from "node:assert/strict"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; export const testClassesAreNominal = () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "tcm-classes-")); fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ type: "module" })); const file = path.join(dir, "errors.ts"); fs.writeFileSync(file, `export class UsageError extends Error {}\nexport class ConflictError extends Error {}\nexport const a = () => 1;\nexport const b = () => 1;\nexport const x = 1;\nexport const y = 1;\n`); const a = analyzeProject([file], { cacheDir: dir, resolveName: () => "" }); const h = Object.fromEntries(a.defs.map((d) => [d.nameHint, d.hash])); assert.notEqual(h.UsageError, h.ConflictError, "structurally identical classes stay distinct"); assert.notEqual(h.a, h.b, "a function's name is observable (`a.name`), so it is hashed too"); assert.equal(h.x, h.y, "a plain value's binding name is not observable"); }