{"record":{"id":"3b50d8a3981cd6de","repo":"oxc-project/oxc","slug":"change-to-throw-new-typeerror","errorCode":null,"errorMessage":"Change to `throw new TypeError(...)`","messagePattern":"Change to `throw new TypeError\\(\\.\\.\\.\\)`","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"error","filePath":"crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs","lineNumber":16,"sourceCode":"use oxc_ast::{\n    AstKind,\n    ast::{CallExpression, Expression, MemberExpression, match_member_expression},\n};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_span::{GetSpan, Span};\nuse oxc_syntax::operator::{BinaryOperator, UnaryOperator};\n\nuse crate::{AstNode, context::LintContext, rule::Rule};\n\nfn prefer_type_error_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\n        \"Prefer throwing a `TypeError` over a generic `Error` after a type checking if-statement\",\n    )\n    .with_help(\"Change to `throw new TypeError(...)`\")\n    .with_label(span)\n}\n\n#[derive(Debug, Default, Clone)]\npub struct PreferTypeError;\n\ndeclare_oxc_lint!(\n    /// ### What it does\n    ///\n    /// Enforce throwing a `TypeError` instead of a generic `Error` after a type checking if-statement.\n    ///\n    /// ### Why is this bad?\n    ///\n    /// Throwing a `TypeError` instead of a generic `Error` after a type checking if-statement is more specific and helps to catch bugs.\n    ///\n    /// ### Examples\n    ///\n    /// Examples of **incorrect** code for this rule:","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/unicorn/prefer_type_error.rs#L1-L34","documentation":"Diagnostic from oxlint's vue/no-watch-after-await rule (crates/oxc_linter/src/rules/vue/no_watch_after_await.rs). It reports `watch` and `watchEffect` calls that appear after an `await` expression inside a `setup()` function of a plain `<script>` block. Vue binds watchers to the current component instance, which only exists synchronously during setup; after the first `await` that context is lost, so the watcher may fail to register or never auto-dispose.","triggerScenarios":"A `.vue` file with a non-setup `<script>` where `export default {...}` or `defineComponent({...})` declares an async `setup()` (function or arrow) that awaits and then calls `watch(...)`/`watchEffect(...)` as a direct expression statement. Only identifiers imported from the module 'vue' count (aliases like `watch as A` are tracked; `watch?.(...)` is flagged). Calls wrapped in other expressions (`const a = watch()`, `d(watch())`, object/array literals) and calls inside nested functions are intentionally ignored, and `<script setup>` blocks are not checked.","commonSituations":"Fetching data in setup() before subscribing to it; refactors that introduce top-level-style awaits into Options API setup; porting eslint-plugin-vue configs to oxlint; moving watchers below an await during async initialization work.","solutions":["Move every `watch`/`watchEffect` call above the first `await` in `setup()`.","If the watcher needs awaited data, register it inside `onMounted` (or another lifecycle hook) instead of after the await.","Keep setup() synchronous and move async work into a composable invoked from `onMounted`, returning the watcher stop-handle for cleanup.","If the placement is intentional and verified safe, suppress with an inline oxlint disable comment for vue/no-watch-after-await."],"exampleFix":"// before\n<script>\nimport { watch } from 'vue'\nexport default {\n  async setup() {\n    await loadUser()\n    watch(user, () => {}) // reported\n  }\n}\n</script>\n\n// after\n<script>\nimport { watch } from 'vue'\nexport default {\n  async setup() {\n    watch(user, () => {})\n    await loadUser()\n  }\n}\n</script>","handlingStrategy":"validation","validationCode":"// pre-commit gate: flag .vue files whose setup() awaits before a watch call\nconst fs = require('fs');\nfor (const f of process.argv.slice(2)) {\n  const src = fs.readFileSync(f, 'utf8');\n  const setupIdx = src.indexOf('setup(');\n  if (setupIdx === -1) continue;\n  const body = src.slice(setupIdx);\n  const awaitIdx = body.search(/\\bawait\\b/);\n  const watchIdx = body.search(/\\b(watch|watchEffect)\\s*(\\?\\.)?\\s*\\(/);\n  if (awaitIdx !== -1 && watchIdx > awaitIdx) {\n    console.warn(`${f}: watch/watchEffect appears after an await in setup()`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call all watch/watchEffect at the top of setup(), before any await.","Do async data loading in onMounted or a composable, not inline after awaits in setup().","Run oxlint with the vue plugin in CI so vue/no-watch-after-await gates every merge.","Prefer <script setup> top-level await (compiler preserves instance context) over async setup()."],"tags":["vue","oxlint","lint","watch","watcheffect","async","setup","composition-api"],"backgroundTag":"vue-watch-after-await","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}