{"_id":"@ashgw/decorators","name":"@ashgw/decorators","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@ashgw/decorators","version":"1.0.0","description":"TypeScript decorators for Final, Frozen, Sealed, and Singleton patterns","main":"dist/index.js","module":"dist/index.js","types":"dist/index.d.ts","type":"module","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsc","clean":"rm -rf dist","prebuild":"npm run clean","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","lint":"biome check . && tsc --noEmit","lint:fix":"biome check --write .","format":"biome format .","format:write":"biome format --write .","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm run test && npm run lint","release":"npm publish --access public"},"keywords":["typescript","decorators","final","frozen","sealed","singleton","patterns"],"author":{"name":"ashgw"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/ashgw/decorators.git"},"bugs":{"url":"https://github.com/ashgw/decorators/issues"},"homepage":"https://github.com/ashgw/decorators#readme","devDependencies":{"@biomejs/biome":"^1.9.4","@types/bun":"latest","typescript":"^5.6.3","vitest":"^2.0.0","@vitest/coverage-v8":"^2.0.0"},"dependencies":{"ts-roids":"^1.51.0"},"peerDependencies":{"typescript":"^5"},"engines":{"node":">=18"},"_id":"@ashgw/decorators@1.0.0","gitHead":"aacbc7cdfdd5035b794cb4a82a9fb85239b994ff","_nodeVersion":"22.18.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-IqVnkR7OD69x8X3KNGoJl1lO3RSA2Yn2KomqCxgY+khp3CiqoSDKhFeuFRqCqyBqyg6gmG/28Epc4XC6KPp17g==","shasum":"e16217e96be1d72bd1fe0f7df6825affa7e0664f","tarball":"https://registry.npmjs.org/@ashgw/decorators/-/decorators-1.0.0.tgz","fileCount":7,"unpackedSize":17524,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDhDhjWTIiIqqHzKHoquwpUFGxplHVyaEsWIx8bE47C1QIhANAL5E7drFCMpGWK/GVCXYXRJ/1eUkzahayKgtMu700C"}]},"_npmUser":{"name":"ashgw","email":"ashrefgw@proton.me"},"directories":{},"maintainers":[{"name":"ashgw","email":"ashrefgw@proton.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/decorators_1.0.0_1755817551761_0.09111274257946911"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-21T23:05:51.638Z","1.0.0":"2025-08-21T23:05:51.950Z","modified":"2025-08-21T23:05:52.238Z"},"maintainers":[{"name":"ashgw","email":"ashrefgw@proton.me"}],"description":"TypeScript decorators for Final, Frozen, Sealed, and Singleton patterns","homepage":"https://github.com/ashgw/decorators#readme","keywords":["typescript","decorators","final","frozen","sealed","singleton","patterns"],"repository":{"type":"git","url":"git+https://github.com/ashgw/decorators.git"},"author":{"name":"ashgw"},"bugs":{"url":"https://github.com/ashgw/decorators/issues"},"license":"MIT","readme":"# @ashgw/decorators\n\nWhile TypeScript supports experimental decorators, it still lacks fundamental modifiers like final, sealed, or frozen that exist in other languages. This package patches those gaps with pragmatic decorators that enforce runtime and semantic constraints you'd often expect but cannot express in TS out of the box.\n\n\n### Installation\n\n**npm**\n```\nnpm i @ashgw/decorators\n```\n**pnpm**\n```\npnpm i @ashgw/decorators\n```\n\n#### Setup\n\nSet this in your TypeScript config file.\n\n```json\n{\n  \"compilerOptions\": {\n    // ...\n    \"experimentalDecorators\": true\n  }\n}\n```\n\n#### Decorators\n\n- [`@Final`]() - Marks an object final, as in one cannot inherit from it.\n- [`@Sealed`]() - [Seals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) an object.\n- [`@Frozen`]() - [Freezes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) an object.\n- [`@Singleton`]() - Ensures that only a single instance of the class can be created.\n\n#### Basic Usage\n\nFinalize and freeze objects\n\n```ts\nimport type { Optional, NewType, MaybeUndefined } from \"ts-roids\";\nimport { Final, Frozen, Singleton } from \"@ashgw/decorators\";\n\ntype Bar = NewType<\"Bar\", string>;\ntype Baz = NewType<\"Baz\", string>;\ntype Secret = NewType<\"Secret\", string>;\n\nabstract class BaseFoo<T> {\n  public abstract requestFoo(secret: Secret, baz: Baz): Promise<Optional<T>>;\n}\n\n@Final\n@Frozen\n@Singleton\nclass Foo<T> extends BaseFoo<T> {\n  private static readonly rnd = Math.random();\n  private readonly foo: T;\n  public bar: Optional<Bar>; // `Bar` then becomes readonly with the decorator\n\n  public constructor(foo: T, bar?: MaybeUndefined<Bar>) {\n    super();\n    this.foo = foo;\n    this.bar = bar ?? null;\n  }\n\n  public override async requestFoo(\n    secret: Secret,\n    baz: Baz\n  ): Promise<Optional<T>> {\n    if (\n      Foo.rnd > 0.5 &&\n      secret.concat().toLowerCase() === \"123\" &&\n      baz.concat().toLowerCase() === \"baz\" &&\n      this.bar !== null\n    ) {\n      return await Promise.resolve(this.foo);\n    }\n\n    return null;\n  }\n}\n\nclass SubFoo extends Foo<string> {\n  constructor(foo: string) {\n    super(foo);\n  }\n}\n\n// No problem with instantiation\nconst foo = new Foo(\"foo\");\n\n// The Singleton ensures the same instance is returned\nconst foo2 = new Foo(\"bar\");\nconsole.log(foo2 === foo); // True\n\n// Since the object is final:\n// The line below will cause a TypeError: Cannot inherit from the final class Foo\nnew SubFoo(\"subFoo\");\n\n// Since the object is frozen:\n// The line below will cause a TypeError: Cannot add property 'requestFoo', object is not extensible\nfoo.requestFoo = async () => {\n  return await Promise.resolve(\"not foo\");\n};\n\n// The line below will cause a TypeError: Cannot assign to read only property 'bar'\nfoo.bar = \"not bar\" as Bar;\n```\n\nThe TypeScript team has not yet introduced a built-in final modifier yet, check\n[this](https://github.com/microsoft/TypeScript/issues/8306), [this](https://github.com/microsoft/TypeScript/issues/50532) and many other requests.\nAlthough they introduced `override` in [`v4.3`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html#override-and-the---noimplicitoverride-flag) .\n\nDecorators like `@Final` provide a limited way to emulate final behavior, these are merely band-aids for now, until TS officially supports a true final modifier.\n\nAnother use case is sealing an object:\n\n```ts\n@Sealed\nclass Person {\n  constructor(name: string, age?: number) {}\n}\n\nconst john = new Person(\"John\", 30);\n\n// Existing properties can still be modified\njohn.age = 31; // No Errors\n\n// Existing properties cannot be re-configured nor deleted\n\n(john as any).email = \"john@doe.com\"; // TypeError: Cannot add property email,\n// object is not extensible\n\ndelete john.age; // TypeError: Cannot delete property 'age'\n```\n## License\n[MIT](/LICENSE)\n\n","readmeFilename":"README.md","_rev":"1-c03265cad4d01394f4158fa0a6478fe2"}