<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dominic Elm</title>
    <description>The latest articles on DEV Community by Dominic Elm (@d3lm).</description>
    <link>https://dev.to/d3lm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F195094%2F4c997750-6e1b-416b-889f-5be4c8c64113.jpg</url>
      <title>DEV Community: Dominic Elm</title>
      <link>https://dev.to/d3lm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/d3lm"/>
    <language>en</language>
    <item>
      <title>What makes AI-generated code slop?</title>
      <dc:creator>Dominic Elm</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:00:13 +0000</pubDate>
      <link>https://dev.to/d3lm/what-makes-ai-generated-code-slop-40on</link>
      <guid>https://dev.to/d3lm/what-makes-ai-generated-code-slop-40on</guid>
      <description>&lt;p&gt;Suppose we ask an agent to write a &lt;code&gt;saveAll()&lt;/code&gt; function to report success only after every item has been saved. It gives us this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's an &lt;code&gt;async&lt;/code&gt; function, and an &lt;code&gt;await&lt;/code&gt; next to the database call. At a glance, the code has the expected shape, right? But &lt;code&gt;forEach()&lt;/code&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#description" rel="noopener noreferrer"&gt;doesn't wait for promises&lt;/a&gt;. This function can report success while writes are still pending. A rejected save won't reject the promise returned by &lt;code&gt;saveAll()&lt;/code&gt;, either.&lt;/p&gt;

&lt;p&gt;A test that checks for &lt;code&gt;{ ok: true }&lt;/code&gt; and confirms that &lt;code&gt;database.save()&lt;/code&gt; was called for each item can still pass. We now have code and tests that look finished without checking the thing the requirement asked for.&lt;/p&gt;

&lt;p&gt;That's the type of problem I want to talk about. We can generate code that &lt;em&gt;looks&lt;/em&gt; finished before anyone has properly checked it, and someone else ends up doing the checking and fixing. Is this code that a modern frontier or open-weight model would generate? Very likely not but the point stands. It's code that looks convincingly ready and plausible, yet it's not. AI makes it much easier to produce that work faster than other people can verify it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I mean by slop
&lt;/h2&gt;

&lt;p&gt;Everyone is talking about AI slop, and I'm increasingly unsure whether we're talking about the same thing. Some people mean broken code, others mean unnecessary complexity, or a pull request whose author can't explain what they've submitted. The label alone tells us very little about what needs fixing.&lt;/p&gt;

&lt;p&gt;I went back and forth on a definition for a while. I didn't invent the term "slop", but I wanted to bring together the recurring problems I've seen:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;LLM code slop is the production of AI-generated software artifacts whose apparent plausibility or production speed is not matched by verified semantic, architectural, security, or maintainability quality. This shifts a disproportionate share of validation, debugging, or maintenance costs to other people or systems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ironically, AI helped me turn my own rough blurb into that formal definition.&lt;/p&gt;

&lt;p&gt;By "production", I mean the way we produce and hand over work. Finding a bug in an AI-assisted change doesn't, by itself, make the contribution slop. Bugs slip through good engineering practices too, and human-written code can be slop. The problem starts when we call work finished without checking it well enough for its intended use.&lt;/p&gt;

&lt;p&gt;Software artifacts includes code, but also tests and design documents. A misleading vulnerability report can consume engineering time without adding a single line to a codebase. Generated tests can make an incorrect implementation look convincing.&lt;/p&gt;

&lt;p&gt;"Apparent plausibility or production speed" describes how finished the work looks and how quickly we can generate it. Familiar code patterns and a passing test suite make a change easy to accept. Neither tells us how thoroughly it was checked.&lt;/p&gt;

&lt;p&gt;Semantic quality asks whether the code does what the requirement says. For &lt;code&gt;saveAll()&lt;/code&gt;, starting every write is different from completing every write successfully.&lt;/p&gt;

&lt;p&gt;Architectural quality asks how well the change fits the surrounding system. A new helper can work correctly on its own and still duplicate a policy the repository already has.&lt;/p&gt;

&lt;p&gt;Security asks whether the system prevents behavior it must not allow, including when someone deliberately tries to misuse it. Checking that the intended user can perform an action leaves open whether an unauthorized user can do it too.&lt;/p&gt;

&lt;p&gt;Maintainability asks whether another engineer can understand and safely change what we've added. An implementation can produce the right result today while making future changes harder to reason about.&lt;/p&gt;

&lt;p&gt;"Disproportionate" leaves room for collaboration. Reviewers should ask questions, and new contributors should get help. I'd call it slop when an author skips verification they should have done and leaves that work to the reviewer or whoever has to maintain the system.&lt;/p&gt;

&lt;p&gt;How much verification is enough depends on what happens when the software goes wrong. A rough prototype with explicit limits can be appropriate for trying out an idea. A payment system that charges someone twice can leave them short on rent money. A clinical system that mixes up patient records can lead to treatment decisions based on the wrong information. Those systems need much more thorough checks, including what happens when something fails. AI-assisted code can meet those standards, but carrying prototype shortcuts into software people rely on creates work and risks for others.&lt;/p&gt;

&lt;h2&gt;
  
  
  Someone has to do the checking
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://x.com/bagder" rel="noopener noreferrer"&gt;Daniel Stenberg&lt;/a&gt;'s experience with curl makes that cost pretty visible. In his post, &lt;a href="https://daniel.haxx.se/blog/2025/07/14/death-by-a-thousand-slops" rel="noopener noreferrer"&gt;Death by a thousand slops&lt;/a&gt;, he described the burden of low-quality vulnerability reports. Investigating a report involved three or four members of the security team, with each person spending perhaps half an hour and sometimes up to three hours. Some team members had only a few hours a week available for curl.&lt;/p&gt;

&lt;p&gt;Those reports cost attention even when the claimed vulnerability didn't exist. Someone had to investigate and establish why the claim was wrong. Stenberg also described poor human submissions with the same effect. Generating a convincing report is cheap compared with the work needed to verify it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests need checking too
&lt;/h2&gt;

&lt;p&gt;For many changes, we rely on unit, integration, and end-to-end tests. Agents can generate plenty more alongside the implementation. But the tests themselves don't always get the same scrutiny as the code they're supposed to verify. A handful of people I talked to told me they skip spec files completely when reviewing a pull request. Once everything passes, it's easy to assume the change has been properly checked.&lt;/p&gt;

&lt;p&gt;Just recently I came across a blog post that really resonated with me. In &lt;a href="https://www.michaelbromley.co.uk/blog/the-problem-with-your-ai-tests/" rel="noopener noreferrer"&gt;The Problem With Your AI Tests&lt;/a&gt;, &lt;a href="https://x.com/michlbrmly" rel="noopener noreferrer"&gt;Michael Bromley&lt;/a&gt; gives an example of a discount test that checks whether the resulting total is non-negative. That assertion accepts all sorts of incorrect totals. The test looks relevant, runs successfully, and still doesn't establish that the discount was calculated correctly.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;saveAll()&lt;/code&gt; tests have the same problem. Checking the return value and counting calls tells us nothing about when the writes finish. A capable model might spot the &lt;code&gt;forEach()&lt;/code&gt; mistake immediately, but the same timing bug can be harder to find across several functions.&lt;/p&gt;

&lt;p&gt;If running all writes at once is appropriate for this workload, we can use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" rel="noopener noreferrer"&gt;&lt;code&gt;Promise.all()&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming &lt;code&gt;database.save()&lt;/code&gt; fulfills only when a write succeeds, this reports success only after every write succeeds. If a write rejects, &lt;code&gt;saveAll()&lt;/code&gt; rejects too. &lt;code&gt;Promise.all()&lt;/code&gt; rejects on the first failure; it doesn't cancel the other writes or roll back writes that already succeeded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled" rel="noopener noreferrer"&gt;&lt;code&gt;Promise.allSettled()&lt;/code&gt;&lt;/a&gt; waits for every write to settle, but a rejected write becomes an entry in the results array. It doesn't make the combined promise reject. Swapping it into the code above and still returning &lt;code&gt;{ ok: true }&lt;/code&gt; would report success even after failed writes. We'd need to inspect the results and throw or return a failure when any write failed.&lt;/p&gt;

&lt;p&gt;The code also starts every write at once, which may exceed the database's limits. The requirement needs to establish the supported workload and how to handle partial failure. Correct promise handling alone doesn't settle those decisions.&lt;/p&gt;

&lt;p&gt;A useful test would keep at least one write pending and check that &lt;code&gt;saveAll()&lt;/code&gt; hasn't reported success, then complete the writes and check the result. Another would reject a write and check that the failure reaches the caller. These tests need to fail against the original version. Otherwise, we still haven't established that they catch the bug.&lt;/p&gt;

&lt;p&gt;Once I'm happy with a plan, I often ask a separate agent to write the tests before I let another agent work on the implementation. That requires a clearly defined interface and expected behavior. In my experience, having no implementation to look at helps the agent write better black-box tests, focused on what the code should do without depending on how it works internally.&lt;/p&gt;

&lt;p&gt;But black-box tests can be wrong too. The test-writing agent can misunderstand the requirement, assume the wrong expected result, or miss a failure case. Two agents working from the same ambiguous plan can agree on the same mistake. I still need to review the expected results against the requirements. For a discount calculation, that means checking the expected total against the pricing rules. For &lt;code&gt;saveAll()&lt;/code&gt;, it means agreeing on what success and failure mean before either agent starts.&lt;/p&gt;

&lt;p&gt;Once those acceptance tests are reviewed, I'd lock them for the implementation step. Keep the approved copy outside the implementation agent's write access and have an independent check run that copy against its changes. Telling the agent "don't edit the tests" helps, but it doesn't enforce that boundary.&lt;/p&gt;

&lt;p&gt;The agent can propose a correction when a test is wrong, and it can add white-box tests for internal behavior. Those additions need review too. Changes to the approved expectations need a separate review against the requirements, including changes to fixtures or test configuration that could weaken what gets checked.&lt;/p&gt;

&lt;p&gt;Reward hacking is one reason to protect that boundary. A model can learn to exploit an evaluation's loopholes to get rewarded without solving the task. &lt;a href="https://openai.com/index/chain-of-thought-monitoring" rel="noopener noreferrer"&gt;OpenAI documented an agent changing a verification function to always return &lt;code&gt;true&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://www.anthropic.com/research/emergent-misalignment-reward-hacking" rel="noopener noreferrer"&gt;Anthropic demonstrated learned cheating in a controlled training setup with known loopholes&lt;/a&gt;. Those experiments don't establish how often this happens in everyday use. OpenAI also &lt;a href="https://openai.com/index/how-we-monitor-internal-coding-agents-misalignment" rel="noopener noreferrer"&gt;reported improper test edits in internal deployments&lt;/a&gt; in March 2026, classifying them as uncommon. A weak test may simply reflect a misunderstanding. In either case, changes that make a test easier to pass deserve scrutiny.&lt;/p&gt;

&lt;p&gt;I'm also using mutation testing to check whether tests catch the failures they're supposed to. The idea is to introduce a small, deliberate fault into working code and rerun the tests. If a relevant test fails, it caught the mutation. If the tests still pass, we need to investigate what they missed. For &lt;code&gt;saveAll()&lt;/code&gt;, useful faults to try include returning before writes finish or swallowing a write failure. For a retry policy, we could allow one more attempt than the agreed limit. These can be targeted mutations applied by hand when the tool doesn't generate them.&lt;/p&gt;

&lt;p&gt;I've put that workflow into my &lt;a href="https://github.com/d3lm/ai-lab/tree/main/skills/mutation-test-review" rel="noopener noreferrer"&gt;mutation-test-review skill&lt;/a&gt;. For now, it supports Rust through &lt;a href="https://mutants.rs" rel="noopener noreferrer"&gt;cargo-mutants&lt;/a&gt; and TypeScript/JavaScript through &lt;a href="https://github.com/stryker-mutator/stryker-js" rel="noopener noreferrer"&gt;StrykerJS&lt;/a&gt;. It reviews the assertions, runs mutations against the relevant code, and investigates survivors. It also checks whether the mutation run actually executed the expected tests and verifies suggested tests against the faults they're meant to catch.&lt;/p&gt;

&lt;p&gt;A mutation score still needs interpretation. Some mutations &lt;a href="https://stryker-mutator.io/docs/mutation-testing-elements/equivalent-mutants/" rel="noopener noreferrer"&gt;leave the behavior unchanged&lt;/a&gt;, so no test could detect them. Others fail to compile or affect code whose tests weren't included in the run. A high score only tells us about the mutations and tests we ran. It can't establish that our requirements are right or cover a behavior nobody thought to specify. I want to know which realistic faults get through and what test would catch them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code that passes and still creates work
&lt;/h2&gt;

&lt;p&gt;Some failures are harder to expose with a failing test, especially in complex systems. Suppose a repository already has a retry policy, say exponential backoff, but an agent adds another implementation elsewhere in the codebase. The new code might handle every example in the prompt correctly, but the team and our agents now have to maintain the same behavior in two places. This means a future change to retry rules now requires someone to find both implementations and work out whether their differences are intentional. The agent may simply have failed to discover the existing retry utilities.&lt;/p&gt;

&lt;p&gt;This is one part of getting a repo "agent ready", which is to make it clear how shared problems are supposed to be solved. If retries go through an existing policy, the code and documentation should make that easy to find and reuse. In practice, this means clear repository structure and documented conventions which help agents plan and check their work.&lt;/p&gt;

&lt;p&gt;I find &lt;a href="https://openai.com/index/harness-engineering/" rel="noopener noreferrer"&gt;OpenAI's write-up on harness engineering&lt;/a&gt; really interesting, and it describes why this matters in practice. The team found that agents copied existing patterns, including the ones they didn't want spreading through the codebase. They introduced rules that favor shared utility packages over separate helpers and enforced architectural boundaries with custom linters and structural tests. That gives agents feedback when a change breaks a repository rule, without waiting for someone to point it out in review.&lt;/p&gt;

&lt;p&gt;For the retry example, I'd want the agent to find the existing policy before writing another one and explain why retries need to be handled differently. Reusing the same patterns for the same problems makes that decision easier for both people and agents. Where we can enforce a rule automatically, we should. We still need reviews for decisions the tooling can't make or for which it's missing important context, including whether two similar implementations really need to stay separate.&lt;/p&gt;

&lt;p&gt;I have also seen a lot of unnecessary defensive code which creates similar work. If an internal contract guarantees a string, then adding layers of type checks and a broad catch that returns an empty string needs a very specific reason. In my opinion, agents operate on a lot of assumptions that don't necessarily hold up in the real world, and that makes the code much harder to understand and maintain.&lt;/p&gt;

&lt;p&gt;But even worse, this kind of code may hide a broken assumption that the system should actually expose. Reviewers now have to understand fallback behavior that the requirement never asked for.&lt;/p&gt;

&lt;p&gt;I've also noticed PRs getting rubber-stamped without a proper review. If a manual review amounts to clicking approve, we should question whether it adds value. Production staying online gives us some evidence, but a system can stay available while producing incorrect results or becoming harder to maintain.&lt;/p&gt;

&lt;p&gt;Humans still have to take responsibility for production systems, and we need confidence in what we ship. That can come from reviewing the code ourselves, from reliable automated checks and AI review, or from both. If those checks cover the risks of a change, an extra manual review may add very little. Someone still needs to understand what the checks cover and where they fall short.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd change in the workflow
&lt;/h2&gt;

&lt;p&gt;We can give agents clearer requirements and better checks, then use reviews to investigate what those checks couldn't establish. Here are some changes I'd start with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define what has to be true before asking for code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the retry change, agree on which failures are retryable and how many attempts are allowed. Identify the existing policy and decide whether repeating the operation is safe. For &lt;code&gt;saveAll()&lt;/code&gt;, define what success means and what should happen if a write fails. Those decisions should come from the requirements and the system we're changing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents" rel="noopener noreferrer"&gt;Anthropic's January 2026 guide to evaluating agents&lt;/a&gt; recommends well-specified tasks and stable test environments for coding agents. My suggestion is to apply that to each change and agree on an observable result, then decide how we'll check it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Check that the tests can catch the bug.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For a bug fix, run the new regression test against the broken code first. Confirm that it fails for the reason you expect, then make the change and run it again. Writing a test first doesn't help much if it already passes before the fix. Keep the reviewed expectations fixed while implementing, and use mutation testing on the changed behavior to look for faults the tests still miss.&lt;/p&gt;

&lt;p&gt;For more complicated behavior, ask the agent to propose properties that should always hold, and check those against the requirements. A property-based testing tool can then generate inputs to look for exceptions. For retries, we might check that the number of attempts never exceeds the agreed limit across different failure sequences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Make routine verification automatic and visible.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Put the build, type checks, and relevant tests in CI. Have the agent run the appropriate checks while it works, fix failures, and rerun them at the end.&lt;/p&gt;

&lt;p&gt;If there's a UI, giving the agent a browser can be really powerful. Have it run the app and go through the user flow it changed. For a save action, that means submitting the form and reloading the page to check that the change persisted. It should inspect the result, including screenshots when appearance matters. &lt;a href="https://openai.com/index/harness-engineering/" rel="noopener noreferrer"&gt;OpenAI's February 2026 write-up&lt;/a&gt; describes giving agents browser access so they could reproduce bugs and verify fixes directly. GPT 6 Astra is really good with computer use.&lt;/p&gt;

&lt;p&gt;Build the setup that makes this possible. The agent needs a reliable way to start the app, reset test data, and a test account if the flow requires login. Give it access to logs so it can investigate failures. Make checking the changed flow part of finishing the task, and keep a record of what it tried and what happened. If it finds a bug, add a regression test so the next change gets checked for it automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Keep changes focused, and review how they affect the rest of the system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PR size matters a lot, especially now that generating code is so cheap. It's easy to produce a huge PR that takes much longer to understand than it took to generate. Keep PRs small and focused on the problem you're solving, and handle unrelated cleanup separately. That makes reviews easier and gives reviewers a better chance of spotting mistakes. Small, focused PRs also make it easier to trace a bug to the change that introduced it and revert that change without undoing unrelated work. Keeping commits independently testable helps when using &lt;code&gt;git bisect&lt;/code&gt;, too.&lt;/p&gt;

&lt;p&gt;During review, check how the changed code interacts with its call sites and the rest of the system. The retry example shows why this matters: the new code can work correctly on its own and still duplicate something that already exists elsewhere in the repo.&lt;/p&gt;

&lt;p&gt;Passing tests doesn't mean we understand the system we're building. If nobody can explain how the generated code fits into the system or why certain decisions were made, future changes become harder to judge and bugs harder to investigate. That creates a lot of maintenance work too because someone eventually has to reconstruct the understanding we skipped. Agents can do that work too, but when they lack context, they fill the gaps with assumptions unless we correct them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Build an AI reviewer for your codebase, and check that it works.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every project has its own conventions and patterns. An AI reviewer needs to know what those are and which decisions are intentional. Give it focused rules with examples and pointers to the relevant code. Explain things like which layer owns validation or what counts as a breaking API change. &lt;a href="https://docs.github.com/en/copilot/tutorials/customize-code-review" rel="noopener noreferrer"&gt;GitHub's guidance on customising code review&lt;/a&gt; recommends concrete examples and separate instructions where different parts of the repository need different rules.&lt;/p&gt;

&lt;p&gt;If you use &lt;a href="https://www.greptile.com" rel="noopener noreferrer"&gt;greptile&lt;/a&gt; you can define &lt;a href="https://www.greptile.com/docs/how-greptile-works/custom-rules" rel="noopener noreferrer"&gt;custom rules&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is worth investing in! AI reviews catch real bugs, and we should absolutely take advantage of that. I've seen them catch bugs I honestly don't think I would have found myself. With the newer frontier models in particular (GPT 6 Astra or Fable 5.1), I've been impressed by how persistently they dig through the code and follow up on something that looks wrong.&lt;/p&gt;

&lt;p&gt;Build the reviewer up over time. When it flags an intentional pattern, clarify the guidance. When it misses an important check, add a specific rule or a better example. Update or remove instructions as the codebase changes. Keep track of which findings helped and which just created extra work, so you can tell whether those adjustments are improving the reviews.&lt;/p&gt;

&lt;p&gt;Try it on past PRs that introduced bugs, using the code and context available at the time without the later fix or incident explanation. Include some correct changes too, and repeat these checks when you change the model or review instructions. A &lt;a href="https://openai.com/index/datadog/" rel="noopener noreferrer"&gt;January 2026 account of Datadog's approach&lt;/a&gt; describes reconstructing PRs behind past incidents and asking the engineers involved whether the AI's feedback would have helped. That gives us a way to test the reviewer against problems our own team encounters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Invest in the software factory, and let it review before a human does.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's be honest, most of us don't write much code by hand anymore. Agents write it, and the system around them decides how much we can trust what comes out. That system deserves the same engineering investment as the product it builds.&lt;/p&gt;

&lt;p&gt;Points 3 and 5 are parts of that factory. The investment is in wiring them into a pipeline with a fixed order, so a change has to clear each stage before a human gets pulled in. The agent runs the build, the tests, and the changed user flow. Linters, structural tests, the locked acceptance tests, and a mutation run on the changed behavior reject what they can. End-to-end checks can run on every PR as well. &lt;a href="https://www.meticulous.ai" rel="noopener noreferrer"&gt;Meticulous&lt;/a&gt; records sessions from your development and preview environments and replays them against each PR to show how the change affects user flows. &lt;a href="https://tester.army" rel="noopener noreferrer"&gt;TesterArmy&lt;/a&gt; has an agent click through the app based on tests written in plain English and reports back with screenshots and recordings. Then AI reviewers with focused jobs, such as repository conventions, security, or duplicated logic, go through the diff, and a separate step verifies their findings so only confirmed issues reach the author.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://claude.com/blog/code-review" rel="noopener noreferrer"&gt;Anthropic's Code Review&lt;/a&gt; basically works this way. There are several agents that look for bugs in parallel, verify them to filter out false positives, and rank them by severity. Anthropic reports that the share of its internal PRs with substantive review comments went from 16% to 54%. Well, this is a vendor measuring its own product, but I agree with where it draws the line which is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It won't approve PRs — that's still a human call"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So human review stays but it just starts later. By the time someone opens the PR, the routine questions should already have answers, and the PR should show which stages ran and what they found. The reviewer can then spend their attention on what the pipeline couldn't establish, like whether the requirement is right and which tradeoffs we're accepting. That's also what makes their approval worth more than a click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Find the root cause and fix the class of bug.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's easy to throw an agent at a bug report and stop once the reported problem seems to go away. But if similar bugs keep appearing, we need to look for what they have in common. Have the agent reproduce the failure and trace where things first go wrong.&lt;/p&gt;

&lt;p&gt;Ask the agent to look for the same bug elsewhere in the codebase. Before changing anything, confirm those cases have the same cause. If the problem comes from shared code, fix it there. Add tests for the original bug and the other affected cases. Where possible, make the code prevent the mistake, for example by checking incoming data before passing it on. Keep unrelated changes out of the fix.&lt;/p&gt;




&lt;p&gt;AI code can pass its tests and still make a system worse. If it's hard to understand, unsafe to use, or a pain to maintain, someone eventually has to fix that. Calling it finished and leaving that work to someone else is what I mean by slop.&lt;/p&gt;

&lt;p&gt;Preventing that means investing in tests, review, and a shared understanding of the system. AI can help with all of this. Whether we review the code ourselves or rely on verification systems we've built, we're responsible for knowing what those checks establish and what still needs attention.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>codereview</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Dominic Elm</dc:creator>
      <pubDate>Mon, 16 Mar 2026 08:28:08 +0000</pubDate>
      <link>https://dev.to/d3lm/-21j</link>
      <guid>https://dev.to/d3lm/-21j</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/xjamundx/some-thoughts-on-code-review-in-the-ai-age-part-1-2k3m" class="crayons-story__hidden-navigation-link"&gt;Code Review Kinda Sucks Now&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/xjamundx" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F614779%2Fefa75882-14f4-46cb-aeb1-81bb9ab040fb.jpg" alt="xjamundx profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/xjamundx" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jamund Ferguson
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jamund Ferguson
                
                
              
              &lt;div id="story-author-preview-content-3355094" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/xjamundx" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F614779%2Fefa75882-14f4-46cb-aeb1-81bb9ab040fb.jpg" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jamund Ferguson&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/xjamundx/some-thoughts-on-code-review-in-the-ai-age-part-1-2k3m" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 15&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/xjamundx/some-thoughts-on-code-review-in-the-ai-age-part-1-2k3m" id="article-link-3355094"&gt;
          Code Review Kinda Sucks Now
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/codereview"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;codereview&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/softwaredevelopment"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;softwaredevelopment&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/xjamundx/some-thoughts-on-code-review-in-the-ai-age-part-1-2k3m" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/xjamundx/some-thoughts-on-code-review-in-the-ai-age-part-1-2k3m#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            9 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>codereview</category>
      <category>discuss</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Decorators do not work as you might expect 🤔</title>
      <dc:creator>Dominic Elm</dc:creator>
      <pubDate>Tue, 16 Jul 2019 10:05:14 +0000</pubDate>
      <link>https://dev.to/angular/decorators-do-not-work-as-you-might-expect-3gmj</link>
      <guid>https://dev.to/angular/decorators-do-not-work-as-you-might-expect-3gmj</guid>
      <description>&lt;p&gt;While working on a library called &lt;a href="https://github.com/typebytes/ngx-template-streams" rel="noopener noreferrer"&gt;ngx-template-streams&lt;/a&gt;, which in a nutshell allows you to work with events as streams in Angular templates, I have discovered that decorators are not instance-based but rather class-based. I was not aware of this behavior and thought that decorators get applied per class instance. In this blog post, we'll take a closer look at decorators and explore why they behave this way and how we can create instance-based decorators.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by Garett Mizunaka on Unsplash&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Quick Recap on Decorators
&lt;/h1&gt;

&lt;p&gt;Decorators are great. They allow us to add annotations and a meta-programming syntax for class declarations and members, including properties, accessors, parameters, and methods. In other words, we can use decorators to attach additional responsibility to an object without modifying any other object. Therefore, they are great to compose pieces of functionality in a declarative fashion. That means the &lt;a href="https://en.wikipedia.org/wiki/Decorator_pattern" rel="noopener noreferrer"&gt;decorator design pattern&lt;/a&gt; is designed in a way that multiple decorators can be stacked on top of each other, each adding new functionality. &lt;/p&gt;

&lt;p&gt;Also, many people consider decorators as a &lt;strong&gt;flexible alternative to subclassing&lt;/strong&gt;. While subclassing adds behavior at compile-time, and therefore affecting all instances, decorators add behavior to individual objects at &lt;strong&gt;runtime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So decorators have gained much popularity, and quite frankly for a reason. They make our code easier to read, test, and maintain. Thus, some of the leading open source projects have adopted the decorator design pattern, including &lt;a href="https://github.com/angular/angular" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;, &lt;a href="https://github.com/inversify" rel="noopener noreferrer"&gt;Inversify&lt;/a&gt; or &lt;a href="https://github.com/nestjs/nest" rel="noopener noreferrer"&gt;Nest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ok, so what's a decorator?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@idandrd" rel="noopener noreferrer"&gt;Idan Dardikman&lt;/a&gt; summarizes this question wonderfully:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Decorators are just a clean syntax for wrapping a piece of code with a function&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;TypeScript has &lt;strong&gt;experimental&lt;/strong&gt; support for decorators. However, there is an &lt;a href="https://github.com/tc39/proposal-decorators" rel="noopener noreferrer"&gt;ECMAScript decorator proposal&lt;/a&gt; that has reached stage 2 (draft), so they could eventually land in vanilla JS.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, there are different types of decorators. For example, we could attach a decorator to a class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HeroComponent&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@Component()&lt;/code&gt; is an excellent example for a class decorator, and it's one of the core building blocks in &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;. It attaches additional metadata to the class.&lt;/p&gt;

&lt;p&gt;Most likely you'll also encounter some &lt;strong&gt;property&lt;/strong&gt;, &lt;strong&gt;method&lt;/strong&gt; or &lt;strong&gt;parameter&lt;/strong&gt; decorators along the way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HeroComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;someDependency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;deprecated&lt;/span&gt;
  &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello there!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So decorators are quite universal, expressive, and powerful. Now, this blog post is not about explaining decorators in all their details. In this post, we implement a property decorator to explore their behavior, but we won't look at the implementation of other types of decorators. If you want to learn more about decorators in general, I highly recommend the &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;, this &lt;a href="https://medium.com/iqoqo-engineering/understand-typescript-decorators-in-5-minutes-26ffc6189082" rel="noopener noreferrer"&gt;gentle introduction&lt;/a&gt; or &lt;a href="https://blog.wizardsoftheweb.pro/tag/typescript/" rel="noopener noreferrer"&gt;this&lt;/a&gt; fabulous series on a variety of topics related to decorators.&lt;/p&gt;

&lt;h1&gt;
  
  
  The @Clamp Decorator
&lt;/h1&gt;

&lt;p&gt;It's time for an example to understand the behavior that I mentioned in the beginning. The claim was that decorators are &lt;strong&gt;not&lt;/strong&gt; instance-targeted and only called &lt;strong&gt;once per class and usage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To proof this, we'll implement our own &lt;a href="https://www.typescriptlang.org/docs/handbook/decorators.html#property-decorators" rel="noopener noreferrer"&gt;property decorator&lt;/a&gt; called &lt;code&gt;Clamp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To use decorators in TypeScript, we have to enable a compiler option called &lt;code&gt;experimentalDecorators&lt;/code&gt;. The best place to do this is the &lt;code&gt;tsconfig.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"experimentalDecorators"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now create a &lt;code&gt;Clamp&lt;/code&gt; decorator that we would apply to properties of type &lt;code&gt;number&lt;/code&gt;. Its job is to clamp the property value within a specified upper and lower bounds.&lt;/p&gt;

&lt;p&gt;For example, if the lower bound is &lt;code&gt;10&lt;/code&gt; and the upper bound is &lt;code&gt;50&lt;/code&gt;, then our decorated should clamp a value within those bounds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; 10&lt;/span&gt;
&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; 50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll implement this functionality later, but first, let's shift our attention to the property decorator.&lt;/p&gt;

&lt;p&gt;A property decorator has the following signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PropertyDecoratorType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a plain old &lt;strong&gt;function&lt;/strong&gt; with two parameters. The &lt;code&gt;target&lt;/code&gt; is the &lt;strong&gt;object&lt;/strong&gt; that &lt;strong&gt;owns&lt;/strong&gt; the decorated property, and the &lt;code&gt;propertyKey&lt;/code&gt; is the &lt;strong&gt;name&lt;/strong&gt; of the decorated property. Now, you might be thinking that the &lt;code&gt;target&lt;/code&gt; is the instance of a class, but that's not quite the case. The &lt;code&gt;target&lt;/code&gt; is simply the &lt;strong&gt;prototype&lt;/strong&gt; of the class, but more on this in just a moment.&lt;/p&gt;

&lt;p&gt;The signature above describes a property decorator, and it's well defined. That means the parameters are fixed, and there's no room for extending the signature. However, our decorator is supposed to be &lt;strong&gt;configurable&lt;/strong&gt; and accept a &lt;code&gt;lower&lt;/code&gt; and &lt;code&gt;upper&lt;/code&gt; bound. Therefore, we have to use the a &lt;strong&gt;factory function&lt;/strong&gt;. In other words, we enclose the decorator method within another method (factory) that defines all configurable options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// logic goes here&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`@Clamp called on '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;' from '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice, we turned a regular decorator into a decorator factory to unleash even more power. Yay!&lt;/p&gt;

&lt;p&gt;Before implementing the logic, let's give it a spin! We'll create a class &lt;code&gt;TestBench&lt;/code&gt; and decorate some properties with our homemade &lt;code&gt;@Clamp&lt;/code&gt; decorator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestBench&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's our simple test bench. Note that we are not creating an instance of the &lt;code&gt;TestBench&lt;/code&gt; class. So before we run this code, let's do a little quiz:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question:&lt;/strong&gt; What do you expect to happen?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A&lt;/strong&gt;: Nothing. The decorator doesn't get called because we are not creating an instance of the class; hence, nothing is logged.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt;: The decorator factory is called &lt;strong&gt;once&lt;/strong&gt; per class; thus, there will only be &lt;strong&gt;one&lt;/strong&gt; value printed to the console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;: The factory is called twice, once per property; hence, there will be &lt;strong&gt;two&lt;/strong&gt; values printed to the console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D&lt;/strong&gt;: It explodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, drum roll... 🥁🥁🥁&lt;/p&gt;

&lt;p&gt;Running this code gives us the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;@Clamp called on 'a' from 'TestBench'
@Clamp called on 'b' from 'TestBench'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tada! Wait, what? So it seems that our decorator function is called &lt;strong&gt;twice&lt;/strong&gt;, once per decorated property. This means the solution to the quiz above is &lt;strong&gt;C&lt;/strong&gt;. In case of doubt, here's a live demo:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/decorators-post-1?view=preview" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The question now is, why, why is the decorator method called without us creating an instance of the class.&lt;/p&gt;

&lt;h1&gt;
  
  
  Exploring decorators under the hood
&lt;/h1&gt;

&lt;p&gt;To find the answer to this question, we have to dive a bit deeper and see what is actually generated by the TypeScript compiler if we use a decorator. You can either run &lt;code&gt;tsc&lt;/code&gt; or copy and paste the code into the &lt;a href="http://www.typescriptlang.org/play/" rel="noopener noreferrer"&gt;TypeScript Playground&lt;/a&gt;. No matter what we do, we should get the following transpiled code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;__decorate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__decorate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyDescriptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decorate&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decorators&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// logic goes here&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`@Clamp called on '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;' from '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestBench&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="nf"&gt;__decorate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;TestBench&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;__decorate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;TestBench&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance this is not easy to grok, especially this somewhat magical &lt;code&gt;__decorate&lt;/code&gt; function defined at the top. But this method is pretty important, especially how it's consumed.&lt;/p&gt;

&lt;p&gt;So where does &lt;code&gt;__decorate&lt;/code&gt; come from and what does it do? This method comes from deep, deep, deep down the compiler and is generated when any type of decorator is used. TypeScript has a helper that produces this code, and it's called &lt;a href="https://github.com/microsoft/TypeScript/blob/master/src/compiler/transformers/ts.ts#L3293-L3304" rel="noopener noreferrer"&gt;&lt;code&gt;decorateHelper&lt;/code&gt;&lt;/a&gt;. Definitely check out the source code. It's a great learning resource.&lt;/p&gt;

&lt;p&gt;Ok, but what does it do? Simply put, it loops over every decorator that is passed in and tries to evaluate them. A detailed explanation is outside the scope of this post. Fortunately, there is an excellent &lt;a href="https://netbasal.com/behind-the-scenes-how-typescript-decorators-operate-28f8dcacb224" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; that explains this in-depth.&lt;/p&gt;

&lt;p&gt;So let's draw our attention to the bottom of the generated code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;__decorate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;TestBench&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;__decorate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nc"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;TestBench&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's where the &lt;code&gt;__decorate&lt;/code&gt; function is consumed. Also, we can see that it's called twice, once per decorated property and both get the same &lt;code&gt;target&lt;/code&gt; passed in, that is &lt;code&gt;TestBench.prototype&lt;/code&gt;. The second argument is the &lt;code&gt;propertyKey&lt;/code&gt; and the last argument is a property descriptor. Here, &lt;code&gt;void 0&lt;/code&gt; is used to pass &lt;code&gt;undefined&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;void&lt;/code&gt; operator takes an expression, evaluates it and returns &lt;code&gt;undefined&lt;/code&gt;. For more information, see the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void" rel="noopener noreferrer"&gt;MDN documentation&lt;/a&gt;. There is also a great &lt;a href="https://stackoverflow.com/questions/7452341/what-does-void-0-mean" rel="noopener noreferrer"&gt;StackOverflow answer&lt;/a&gt; that explains the purpose of this tiny operator compared to just using &lt;code&gt;undefined&lt;/code&gt;. &lt;strong&gt;TL;DR&lt;/strong&gt;: JavaScript isn't a perfect world, and &lt;code&gt;undefined&lt;/code&gt; is not a reserved keyword, meaning that it's just a variable name and we could assign a new value to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the code above is the code that gets generated by the TypeScript compiler, and typically we would load the code in a browser where it gets executed once the file is loaded. In other words, decorators will be applied the moment the classes, in which we use decorators, are loaded. As a result, the decorator (here our property decorator) only has access to the prototype of the class and the property name, but not the instance. This is &lt;strong&gt;by design&lt;/strong&gt;, and it all makes sense, now that we know what gets generated by the compiler.&lt;/p&gt;

&lt;p&gt;So far the key takeaway should be that we now know why decorators are not instance-targeted and instead executed when our JavaScript gets loaded in the browser.&lt;/p&gt;

&lt;p&gt;It's essential to be aware of this because otherwise, we might experience unexpected behavior. To understand this, we'll have to add logic to our decorator.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;The fact that decorators are applied when the class is loaded, not when we create instances, is not incorrect and that's actually by design. So what could possibly go wrong?&lt;/p&gt;

&lt;p&gt;To find this out, we start off by implementing the actual clamp functionality. So let's create a factory called &lt;code&gt;makeClamp&lt;/code&gt; which returns a &lt;code&gt;clamp&lt;/code&gt; function with an &lt;code&gt;upper&lt;/code&gt; and &lt;code&gt;lower&lt;/code&gt; bound. Using a factory function again here makes the functionality more reusable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;makeClamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that this factory returns a &lt;code&gt;clamp&lt;/code&gt; method. Here's an example of how we could use this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeClamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; 0&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;   &lt;span class="c1"&gt;// =&amp;gt; 0&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;   &lt;span class="c1"&gt;// =&amp;gt; 5&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// =&amp;gt; 10&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// =&amp;gt; 10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The examples above should give us a proper understanding of what the decorator is supposed to do. A class property annotated with &lt;code&gt;@Clamp&lt;/code&gt; should clip the property value within an inclusive &lt;code&gt;lower&lt;/code&gt; and &lt;code&gt;upper&lt;/code&gt; bound.&lt;/p&gt;

&lt;p&gt;Simply adding this to the decorator function is not enough, because we want the decorator to be operating on an instance and it's supposed to clamp the value of a property every time it's being set.&lt;/p&gt;

&lt;p&gt;Let's say we didn't know that the &lt;code&gt;target&lt;/code&gt; was only the prototype of a class, so we modify the already existing property on the target using &lt;code&gt;Object.defineProperty&lt;/code&gt;. This will allow us, besides other things, to define a &lt;code&gt;getter&lt;/code&gt; and &lt;code&gt;setter&lt;/code&gt;, which is exactly what we need. Here's what we have to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a desired &lt;code&gt;clamp&lt;/code&gt; method using the factory &lt;code&gt;makeClamp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;maintain some internal state used to store the clamped property value.&lt;/li&gt;
&lt;li&gt;modify the target property using &lt;code&gt;Object.defineProperty&lt;/code&gt; and provide a &lt;code&gt;getter&lt;/code&gt; and &lt;code&gt;setter&lt;/code&gt; so that we can intercept any modification to the value and run it through our &lt;code&gt;clamp&lt;/code&gt; method.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Putting this into code could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`@Clamp called on '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;' from '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 1. Create clamp method&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeClamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Create internal state variable that holds the clamped value&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Modify target property and provide 'getter' and 'setter'. The 'getter'&lt;/span&gt;
    &lt;span class="c1"&gt;// simply returns the internal state, and the 'setter' will run any new value&lt;/span&gt;
    &lt;span class="c1"&gt;// through 'clamp' and update the internal state.&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's also update our test bench, remove one property for simplicity, and create two instances of the test class. Furthermore, we'll set the property to some value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestBench&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tb1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestBench&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Setting 'a' on TB1`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;tb1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Value of 'a' on TB1:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tb1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tb2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestBench&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Value of 'a' on TB2:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tb2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this code will print the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;@Clamp called on 'a' from 'TestBench'
Setting 'a' on TB1
Value of 'a' on TB1: 20
Value of 'a' on TB2: 20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this output seems a bit off, doesn't it? We create the first instance &lt;code&gt;tb1&lt;/code&gt; and immediately set property &lt;code&gt;a&lt;/code&gt; to &lt;code&gt;30&lt;/code&gt;. This results in the &lt;code&gt;setter&lt;/code&gt; to be called, which clamps the value within the specified upper and lower bound. The result should be &lt;code&gt;20&lt;/code&gt;, and that's the case. So far, so good. Then we create another instance &lt;code&gt;tb2&lt;/code&gt; and simply read the property, causing the &lt;code&gt;getter&lt;/code&gt; to be called. Somehow this returns &lt;code&gt;20&lt;/code&gt; even though we haven't set the value on the second instance. Why?&lt;/p&gt;

&lt;p&gt;This is what I meant by &lt;em&gt;unexpected behavior&lt;/em&gt;, at least if we are not aware of the fact that the &lt;code&gt;target&lt;/code&gt; is not the class instance but the prototype. So any modifications on the target will affect every instance because we are globally modifying the prototype of the class. Also, the &lt;code&gt;value&lt;/code&gt; that was meant to be an internal state to every decorator is &lt;strong&gt;shared&lt;/strong&gt; across all instances, because they all &lt;strong&gt;share the same decorator scope&lt;/strong&gt;. It is what it is, but for our use case, that's not cool.&lt;/p&gt;

&lt;p&gt;Check out this live demo! I highly encourage you to noodle around with the code a bit.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/decorators-post-2?view=preview" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Creating instance-targeted decorators
&lt;/h1&gt;

&lt;p&gt;So what do we do if we want our decorator to be instance-based? We certainly don't want to share state across instances globally.&lt;/p&gt;

&lt;p&gt;The solution involves modifying the target property once the decorator is applied, as well as defining a property on the instance with the same property name. In other words, we define a property with a &lt;code&gt;setter&lt;/code&gt; on the target prototype that will install a property with the same name, that is &lt;code&gt;propertyKey&lt;/code&gt;, on the target instance once it is used for the first time.&lt;/p&gt;

&lt;p&gt;Ok, let's take a look at the code. I have added a whole bunch of comments to make it easier to understand what's going on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`@Clamp called on '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;' from '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

     &lt;span class="c1"&gt;// Create clamp method&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeClamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create map to store values associated to a class instance&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WeakMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   

    &lt;span class="c1"&gt;// Define property on the target with only a `setter` because we don't&lt;/span&gt;
    &lt;span class="c1"&gt;// want to read from the prototype but instead from the instance.&lt;/span&gt;
    &lt;span class="c1"&gt;// Once the value of the property is set for the first time we define&lt;/span&gt;
    &lt;span class="c1"&gt;// a property with a `getter` and `setter` on the instance.&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;set on target&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// This `setter` gets called once per new instance, and only the &lt;/span&gt;
        &lt;span class="c1"&gt;// first time we set the value of the target property.&lt;/span&gt;

        &lt;span class="c1"&gt;// Here we have access to the instance `this`, so we define &lt;/span&gt;
        &lt;span class="c1"&gt;// a property with the same name on the class instance.&lt;/span&gt;
        &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get on instance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// This `getter` gets called every time we read the instance property.&lt;/span&gt;
            &lt;span class="c1"&gt;// We simply look up the instance in our map and return its value.&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;set on instance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="c1"&gt;// This `setter` is called every time we set the value of the &lt;/span&gt;
            &lt;span class="c1"&gt;// property on the class instance.&lt;/span&gt;
            &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// Finally we set the value of property on the class instance.&lt;/span&gt;
        &lt;span class="c1"&gt;// This will trigger the `setter` on the instance that we defined above.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially, we are using &lt;code&gt;Object.defineProperty&lt;/code&gt; inside &lt;code&gt;Object.defineProperty&lt;/code&gt; but with different objects. The first one uses the &lt;code&gt;target&lt;/code&gt; which is the class prototype, and the second one uses &lt;code&gt;this&lt;/code&gt; which refers to the class instance.&lt;/p&gt;

&lt;p&gt;Also, note that we are using a &lt;code&gt;WeakMap&lt;/code&gt; at the top of the decorator to store the property value for each instance. A &lt;code&gt;WeakMap&lt;/code&gt; is a special kind of &lt;code&gt;Map&lt;/code&gt; but the difference is that a &lt;code&gt;WeakMap&lt;/code&gt; doesn't prevent an object from being garbage collected even though this object is used as the &lt;strong&gt;key&lt;/strong&gt; in the &lt;code&gt;WeakMap&lt;/code&gt;. If you want to learn more, check out &lt;a href="https://javascript.info/map-set-weakmap-weakset" rel="noopener noreferrer"&gt;this&lt;/a&gt; fantastic blog post which explains the differences really well.&lt;/p&gt;

&lt;p&gt;Alright, let's give this revised version of our decorator a spin and see if it is really instance-targeted and if it no longer shares state across all instances of the same class. For that, I have slightly updated our test bench and added a few comments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// When this class gets loaded, the decorator is applied and executed.&lt;/span&gt;
&lt;span class="c1"&gt;// This will define the `setter` for the target property on the prototype&lt;/span&gt;
&lt;span class="c1"&gt;// of this class.&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestBench&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tb1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestBench&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// This should return `undefined` because we didn't define a `getter`&lt;/span&gt;
&lt;span class="c1"&gt;// on the target prototype for this property. We only install a `getter`&lt;/span&gt;
&lt;span class="c1"&gt;// once we set the value for the first time.&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Reading 'a' on TB1`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tb1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// This calls the `setter` for `target.a` and defines a property with &lt;/span&gt;
&lt;span class="c1"&gt;// a `getter` and `setter` on the class instance for the same property.&lt;/span&gt;
&lt;span class="nx"&gt;tb1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// From this moment on, every time we read the value for this property&lt;/span&gt;
&lt;span class="c1"&gt;// we would call the most inner `getter`.&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Reading 'a' on TB1`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tb1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The same applies for updating the value. This will call the `setter`&lt;/span&gt;
&lt;span class="c1"&gt;// that we defined for the property of the class instance.&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Updating 'a' on TB1`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;tb1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Creating a new instance doesn't do anything&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tb2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TestBench&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Remember, we have globally defined a getter for `target.a` and because we&lt;/span&gt;
&lt;span class="c1"&gt;// are operating on a new instance, the target setter will be called which&lt;/span&gt;
&lt;span class="c1"&gt;// will set up the property on the new instance with their own `getter`&lt;/span&gt;
&lt;span class="c1"&gt;// and `setter` methods.&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Setting 'a' on TB2`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;tb2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Reading 'a' on TB2:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tb2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Remains unmodified because every instance has it's own property defined&lt;/span&gt;
&lt;span class="c1"&gt;// with their own `getter` and `setter`&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Reading 'a' on TB1:`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tb1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tada! It seems to be working. We have just implemented our own decorator that works on an instance level rather than being prototype-based. I mean it still involves modifying the prototype, but now every decorator also operates on a single instance, and they are all isolated from one another.&lt;/p&gt;

&lt;p&gt;Check out the final solution and definitely play around with the code:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/decorators-post-3?view=preview" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Bonus
&lt;/h1&gt;

&lt;p&gt;The above illustrates a full-blown solution, but while I was writing this blog post &lt;a href="https://twitter.com/NetanelBasal" rel="noopener noreferrer"&gt;Netanel Basal&lt;/a&gt; pointed out to me a solution that is much more concise and cleaner. It doesn't require a double call to &lt;code&gt;Object.defineProperty&lt;/code&gt;, because he &lt;a href="https://github.com/microsoft/TypeScript/issues/32395" rel="noopener noreferrer"&gt;found out&lt;/a&gt; that the return value is not ignored, as opposed to what's mentioned in the documentation, and is in fact used as an input for a call to &lt;code&gt;Object.defineProperty&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that in mind, we can reduce our solution from above to the following, which has the exact same behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propertyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeClamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// We need a unique key here because otherwise we would be&lt;/span&gt;
    &lt;span class="c1"&gt;// calling ourselves, and that results in an infinite loop.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// We can return a property descriptor that is used to define &lt;/span&gt;
    &lt;span class="c1"&gt;// a property on the target given the `propertyKey`.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Read the value from the target instance using the&lt;/span&gt;
        &lt;span class="c1"&gt;// unique symbol from above&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; 
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="c1"&gt;// Clamp the value and write it onto the target instance&lt;/span&gt;
        &lt;span class="c1"&gt;// using the unique symbol from above&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this is pretty clean, isn't it? 🔥&lt;/p&gt;

&lt;p&gt;Here's a live demo:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/decorators-post-4?view=preview" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Decorators are class and property-based, meaning they are applied and executed once per decorated property when the class gets loaded. This means the &lt;code&gt;target&lt;/code&gt; is not the class instance but the prototype of the class. Any changes made to the &lt;code&gt;target&lt;/code&gt; are made globally, and if we try to use the decorator scope to maintain some internal state, that state is being shared across all instances of the same class, and they all use the same decorator scope. This could lead to unexpected behavior.&lt;/p&gt;

&lt;p&gt;However, in this article, we have seen a solution that involves a double &lt;code&gt;Object.defineProperty&lt;/code&gt; with different targets to make a decorator instance-based.&lt;/p&gt;

&lt;p&gt;Hopefully, by now, you have a better understanding of how decorators work and why they behave the way they do.&lt;/p&gt;

&lt;p&gt;If you enjoyed this post feel free to give it a thumbs up and let me know if you have any questions or comments!&lt;/p&gt;

&lt;h1&gt;
  
  
  Special Thanks
&lt;/h1&gt;

&lt;p&gt;I’d like to thank &lt;a href="https://twitter.com/NetanelBasal" rel="noopener noreferrer"&gt;Netanel Basal&lt;/a&gt; and &lt;a href="https://twitter.com/ManfredSteyer" rel="noopener noreferrer"&gt;Manfred Steyer&lt;/a&gt; for reviewing the article and providing valuable feedback. 🙏&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>angular</category>
    </item>
  </channel>
</rss>
