diff --git a/.eslintrc.json b/.eslintrc.json
index 819d042c25b16..a6d1b6345b452 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -105,9 +105,14 @@
                 }
             }
         ],
-
-        // Todo: For each of these, investigate whether we want to enable them ✨
-        "@typescript-eslint/no-unused-vars": "off",
+        "@typescript-eslint/no-unused-vars": [
+            "error",
+            {
+                // Ignore: (solely underscores | starting with exactly one underscore)
+                "argsIgnorePattern": "^(_+$|_[^_])",
+                "varsIgnorePattern": "^(_+$|_[^_])"
+            }
+        ],
 
         // Pending https://github.com/typescript-eslint/typescript-eslint/issues/4820
         "@typescript-eslint/prefer-optional-chain": "off",
diff --git a/scripts/eslint/rules/argument-trivia.cjs b/scripts/eslint/rules/argument-trivia.cjs
index 8a6d1a8f112ca..423da853e212a 100644
--- a/scripts/eslint/rules/argument-trivia.cjs
+++ b/scripts/eslint/rules/argument-trivia.cjs
@@ -1,7 +1,11 @@
-const { AST_NODE_TYPES, TSESTree, ESLintUtils } = require("@typescript-eslint/utils");
+const { AST_NODE_TYPES, ESLintUtils } = require("@typescript-eslint/utils");
 const { createRule } = require("./utils.cjs");
 const ts = require("typescript");
 
+/**
+ * @typedef {import("@typescript-eslint/utils").TSESTree.CallExpression | import("@typescript-eslint/utils").TSESTree.NewExpression} CallOrNewExpression
+ */
+
 const unset = Symbol();
 /**
  * @template T
@@ -42,7 +46,7 @@ module.exports = createRule({
 
         /** @type {(name: string) => boolean} */
         const isSetOrAssert = name => name.startsWith("set") || name.startsWith("assert");
-        /** @type {(node: TSESTree.Node) => boolean} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.Node) => boolean} */
         const isTrivia = node => {
             if (node.type === AST_NODE_TYPES.Identifier) {
                 return node.name === "undefined";
@@ -56,7 +60,7 @@ module.exports = createRule({
             return false;
         };
 
-        /** @type {(node: TSESTree.CallExpression | TSESTree.NewExpression) => boolean} */
+        /** @type {(node: CallOrNewExpression) => boolean} */
         const shouldIgnoreCalledExpression = node => {
             if (node.callee && node.callee.type === AST_NODE_TYPES.MemberExpression) {
                 const methodName = node.callee.property.type === AST_NODE_TYPES.Identifier
@@ -97,7 +101,7 @@ module.exports = createRule({
             return false;
         };
 
-        /** @type {(node: TSESTree.Node, i: number, getSignature: () => ts.Signature | undefined) => void} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.Node, i: number, getSignature: () => ts.Signature | undefined) => void} */
         const checkArg = (node, i, getSignature) => {
             if (!isTrivia(node)) {
                 return;
@@ -119,7 +123,7 @@ module.exports = createRule({
             });
 
             const comments = sourceCode.getCommentsBefore(node);
-            /** @type {TSESTree.Comment | undefined} */
+            /** @type {import("@typescript-eslint/utils").TSESTree.Comment | undefined} */
             const comment = comments[comments.length - 1];
 
             if (!comment || comment.type !== "Block") {
@@ -170,7 +174,7 @@ module.exports = createRule({
             }
         };
 
-        /** @type {(node: TSESTree.CallExpression | TSESTree.NewExpression) => void} */
+        /** @type {(node: CallOrNewExpression) => void} */
         const checkArgumentTrivia = node => {
             if (shouldIgnoreCalledExpression(node)) {
                 return;
diff --git a/scripts/eslint/rules/debug-assert.cjs b/scripts/eslint/rules/debug-assert.cjs
index 31a735c246889..ca3a4263d7d60 100644
--- a/scripts/eslint/rules/debug-assert.cjs
+++ b/scripts/eslint/rules/debug-assert.cjs
@@ -1,4 +1,4 @@
-const { AST_NODE_TYPES, TSESTree } = require("@typescript-eslint/utils");
+const { AST_NODE_TYPES } = require("@typescript-eslint/utils");
 const { createRule } = require("./utils.cjs");
 
 module.exports = createRule({
@@ -17,14 +17,14 @@ module.exports = createRule({
     defaultOptions: [],
 
     create(context) {
-        /** @type {(node: TSESTree.Node) => boolean} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.Node) => boolean} */
         const isArrowFunction = node => node.type === AST_NODE_TYPES.ArrowFunctionExpression;
-        /** @type {(node: TSESTree.Node) => boolean} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.Node) => boolean} */
         const isStringLiteral = node => (
             (node.type === AST_NODE_TYPES.Literal && typeof node.value === "string") || node.type === AST_NODE_TYPES.TemplateLiteral
         );
 
-        /** @type {(node: TSESTree.MemberExpression) => boolean} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.MemberExpression) => boolean} */
         const isDebugAssert = node => (
             node.object.type === AST_NODE_TYPES.Identifier
             && node.object.name === "Debug"
@@ -32,7 +32,7 @@ module.exports = createRule({
             && node.property.name === "assert"
         );
 
-        /** @type {(node: TSESTree.CallExpression) => void} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.CallExpression) => void} */
         const checkDebugAssert = node => {
             const args = node.arguments;
             const argsLen = args.length;
diff --git a/scripts/eslint/rules/jsdoc-format.cjs b/scripts/eslint/rules/jsdoc-format.cjs
index b16de44b4aaa5..4753fe9cc84cc 100644
--- a/scripts/eslint/rules/jsdoc-format.cjs
+++ b/scripts/eslint/rules/jsdoc-format.cjs
@@ -1,4 +1,3 @@
-const { TSESTree } = require("@typescript-eslint/utils");
 const { createRule } = require("./utils.cjs");
 
 module.exports = createRule({
@@ -29,7 +28,7 @@ module.exports = createRule({
             return text.startsWith(jsdocStart);
         }
 
-        /** @type {(c: TSESTree.Comment, indexInComment: number) => TSESTree.SourceLocation} */
+        /** @type {(c: import("@typescript-eslint/utils").TSESTree.Comment, indexInComment: number) => import("@typescript-eslint/utils").TSESTree.SourceLocation} */
         const getAtInternalLoc = (c, indexInComment) => {
             const line = c.loc.start.line;
             return {
@@ -44,7 +43,7 @@ module.exports = createRule({
             };
         };
 
-        /** @type {(c: TSESTree.Comment) => TSESTree.SourceLocation} */
+        /** @type {(c: import("@typescript-eslint/utils").TSESTree.Comment) => import("@typescript-eslint/utils").TSESTree.SourceLocation} */
         const getJSDocStartLoc = c => {
             return {
                 start: c.loc.start,
@@ -55,7 +54,7 @@ module.exports = createRule({
             };
         };
 
-        /** @type {(node: TSESTree.Node) => void} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.Node) => void} */
         const checkDeclaration = node => {
             const blockComments = sourceCode.getCommentsBefore(node).filter(c => c.type === "Block");
             if (blockComments.length === 0) {
diff --git a/scripts/eslint/rules/no-in-operator.cjs b/scripts/eslint/rules/no-in-operator.cjs
index cbd357b86823e..b81d346eaa828 100644
--- a/scripts/eslint/rules/no-in-operator.cjs
+++ b/scripts/eslint/rules/no-in-operator.cjs
@@ -1,4 +1,3 @@
-const { TSESTree } = require("@typescript-eslint/utils");
 const { createRule } = require("./utils.cjs");
 
 module.exports = createRule({
@@ -17,7 +16,7 @@ module.exports = createRule({
 
     create(context) {
         const IN_OPERATOR = "in";
-        /** @type {(node: TSESTree.BinaryExpression) => void} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.BinaryExpression) => void} */
         const checkInOperator = node => {
             if (node.operator === IN_OPERATOR) {
                 context.report({ messageId: "noInOperatorError", node });
diff --git a/scripts/eslint/rules/no-keywords.cjs b/scripts/eslint/rules/no-keywords.cjs
index a0032bf5ff102..7aee37ed35dc7 100644
--- a/scripts/eslint/rules/no-keywords.cjs
+++ b/scripts/eslint/rules/no-keywords.cjs
@@ -1,4 +1,4 @@
-const { TSESTree, AST_NODE_TYPES } = require("@typescript-eslint/utils");
+const { AST_NODE_TYPES } = require("@typescript-eslint/utils");
 const { createRule } = require("./utils.cjs");
 
 module.exports = createRule({
@@ -37,12 +37,12 @@ module.exports = createRule({
         /** @type {(name: string) => boolean} */
         const isKeyword = name => keywords.includes(name);
 
-        /** @type {(node: TSESTree.Identifier) => void} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.Identifier) => void} */
         const report = node => {
             context.report({ messageId: "noKeywordsError", data: { name: node.name }, node });
         };
 
-        /** @type {(node: TSESTree.ObjectPattern) => void} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.ObjectPattern) => void} */
         const checkProperties = node => {
             node.properties.forEach(property => {
                 if (
@@ -56,7 +56,7 @@ module.exports = createRule({
             });
         };
 
-        /** @type {(node: TSESTree.ArrayPattern) => void} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.ArrayPattern) => void} */
         const checkElements = node => {
             node.elements.forEach(element => {
                 if (
@@ -69,7 +69,7 @@ module.exports = createRule({
             });
         };
 
-        /** @type {(node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration | TSESTree.FunctionExpression | TSESTree.TSMethodSignature | TSESTree.TSFunctionType) => void} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.ArrowFunctionExpression | import("@typescript-eslint/utils").TSESTree.FunctionDeclaration | import("@typescript-eslint/utils").TSESTree.FunctionExpression | import("@typescript-eslint/utils").TSESTree.TSMethodSignature | import("@typescript-eslint/utils").TSESTree.TSFunctionType) => void} */
         const checkParams = node => {
             if (!node || !node.params || !node.params.length) {
                 return;
diff --git a/scripts/eslint/rules/only-arrow-functions.cjs b/scripts/eslint/rules/only-arrow-functions.cjs
index 9abde8775c08d..d887a05467f51 100644
--- a/scripts/eslint/rules/only-arrow-functions.cjs
+++ b/scripts/eslint/rules/only-arrow-functions.cjs
@@ -1,6 +1,8 @@
-const { AST_NODE_TYPES, TSESTree } = require("@typescript-eslint/utils");
+const { AST_NODE_TYPES } = require("@typescript-eslint/utils");
 const { createRule } = require("./utils.cjs");
 
+/** @typedef {import("@typescript-eslint/utils").TSESTree.FunctionDeclaration | import("@typescript-eslint/utils").TSESTree.FunctionExpression} FunctionDeclarationOrExpression */
+
 module.exports = createRule({
     name: "only-arrow-functions",
     meta: {
@@ -27,10 +29,10 @@ module.exports = createRule({
     }],
 
     create(context, [{ allowNamedFunctions, allowDeclarations }]) {
-        /** @type {(node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression) => boolean} */
+        /** @type {(node: FunctionDeclarationOrExpression) => boolean} */
         const isThisParameter = node => !!node.params.length && !!node.params.find(param => param.type === AST_NODE_TYPES.Identifier && param.name === "this");
 
-        /** @type {(node: TSESTree.Node) => boolean} */
+        /** @type {(node: import("@typescript-eslint/utils").TSESTree.Node) => boolean} */
         const isMethodType = node => {
             const types = [
                 AST_NODE_TYPES.MethodDefinition,
@@ -57,7 +59,7 @@ module.exports = createRule({
             }
         };
 
-        /** @type {(node: TSESTree.FunctionDeclaration | TSESTree.FunctionExpression) => void} */
+        /** @type {(node: FunctionDeclarationOrExpression) => void} */
         const exitFunction = node => {
             const methodUsesThis = stack.pop();
 
diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json
index 40ec90cd8d82b..42dd5ca6e60bb 100644
--- a/scripts/tsconfig.json
+++ b/scripts/tsconfig.json
@@ -13,11 +13,8 @@
         "strict": true,
         "skipLibCheck": true,
         "forceConsistentCasingInFileNames": true,
-        "allowUnusedLabels": false,
         "noImplicitOverride": true,
         "noImplicitReturns": true,
-        "noUnusedLocals": true,
-        "noUnusedParameters": true,
         "allowJs": true,
         "checkJs": true
     },
diff --git a/src/compiler/core.ts b/src/compiler/core.ts
index 9738920df7527..1ee2fefe16048 100644
--- a/src/compiler/core.ts
+++ b/src/compiler/core.ts
@@ -1,5 +1,4 @@
 import {
-    __String,
     CharacterCodes,
     Comparer,
     Comparison,
@@ -286,7 +285,7 @@ export function filter<T>(array: T[], f: (x: T) => boolean): T[];
 /** @internal */
 export function filter<T, U extends T>(array: readonly T[], f: (x: T) => x is U): readonly U[];
 /** @internal */
-export function filter<T, U extends T>(array: readonly T[], f: (x: T) => boolean): readonly T[];
+export function filter<T>(array: readonly T[], f: (x: T) => boolean): readonly T[];
 /** @internal */
 export function filter<T, U extends T>(array: T[] | undefined, f: (x: T) => x is U): U[] | undefined;
 /** @internal */
@@ -294,7 +293,7 @@ export function filter<T>(array: T[] | undefined, f: (x: T) => boolean): T[] | u
 /** @internal */
 export function filter<T, U extends T>(array: readonly T[] | undefined, f: (x: T) => x is U): readonly U[] | undefined;
 /** @internal */
-export function filter<T, U extends T>(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined;
+export function filter<T>(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined;
 /** @internal */
 export function filter<T>(array: readonly T[] | undefined, f: (x: T) => boolean): readonly T[] | undefined {
     if (array) {
@@ -830,7 +829,7 @@ export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Compa
 }
 
 /** @internal */
-export function sortAndDeduplicate<T>(array: readonly string[]): SortedReadonlyArray<string>;
+export function sortAndDeduplicate(array: readonly string[]): SortedReadonlyArray<string>;
 /** @internal */
 export function sortAndDeduplicate<T>(array: readonly T[], comparer: Comparer<T>, equalityComparer?: EqualityComparer<T>): SortedReadonlyArray<T>;
 /** @internal */
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index 7b7f0fcfa8143..6842fb1c8c71c 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -4131,6 +4131,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri
         writeSpace();
         nextPos = emitTokenWithComment(SyntaxKind.AsKeyword, nextPos, writeKeyword, node);
         writeSpace();
+        // eslint-disable-next-line @typescript-eslint/no-unused-vars
         nextPos = emitTokenWithComment(SyntaxKind.NamespaceKeyword, nextPos, writeKeyword, node);
         writeSpace();
         emit(node.name);
diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts
index e952b614251c7..28c88fe2136f3 100644
--- a/src/compiler/factory/utilities.ts
+++ b/src/compiler/factory/utilities.ts
@@ -1675,7 +1675,7 @@ export function createAccessorPropertyGetRedirector(factory: NodeFactory, node:
  *
  * @internal
  */
-export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: readonly Modifier[] | undefined, name: PropertyName, receiver: Expression = factory.createThis()) {
+export function createAccessorPropertySetRedirector(factory: NodeFactory, node: PropertyDeclaration, modifiers: readonly Modifier[] | undefined, name: PropertyName, receiver: Expression = factory.createThis()): SetAccessorDeclaration {
     return factory.createSetAccessorDeclaration(
         modifiers,
         name,
diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts
index db767177462e8..eeff8c96c7bbf 100644
--- a/src/compiler/moduleNameResolver.ts
+++ b/src/compiler/moduleNameResolver.ts
@@ -348,7 +348,7 @@ interface PackageJson extends PackageJsonPathFields {
     version?: string;
 }
 
-function readPackageJsonField<TMatch, K extends MatchingKeys<PackageJson, string | undefined>>(jsonContent: PackageJson, fieldName: K, typeOfTag: "string", state: ModuleResolutionState): PackageJson[K] | undefined;
+function readPackageJsonField<K extends MatchingKeys<PackageJson, string | undefined>>(jsonContent: PackageJson, fieldName: K, typeOfTag: "string", state: ModuleResolutionState): PackageJson[K] | undefined;
 function readPackageJsonField<K extends MatchingKeys<PackageJson, object | undefined>>(jsonContent: PackageJson, fieldName: K, typeOfTag: "object", state: ModuleResolutionState): PackageJson[K] | undefined;
 function readPackageJsonField<K extends keyof PackageJson>(jsonContent: PackageJson, fieldName: K, typeOfTag: "string" | "object", state: ModuleResolutionState): PackageJson[K] | undefined {
     if (!hasProperty(jsonContent, fieldName)) {
diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts
index 420b59071266b..0a95aeafdab37 100644
--- a/src/compiler/watchPublic.ts
+++ b/src/compiler/watchPublic.ts
@@ -258,18 +258,18 @@ export interface ProgramHost<T extends BuilderProgram> {
     getModuleResolutionCache?(): ModuleResolutionCache | undefined;
 
     jsDocParsingMode?: JSDocParsingMode;
-}
-/**
- * Internal interface used to wire emit through same host
- *
- * @internal
- */
-export interface ProgramHost<T extends BuilderProgram> {
+
+    // Internal interface used to wire emit through same host
+
     // TODO: GH#18217 Optional methods are frequently asserted
+    /** @internal */
     createDirectory?(path: string): void;
+    /** @internal */
     writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void;
     // For testing
+    /** @internal */
     storeFilesChangingSignatureDuringEmit?: boolean;
+    /** @internal */
     now?(): Date;
 }
 
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index cf6c72efaca0a..624360c4fc7e0 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -504,7 +504,7 @@ export function convertScriptKindName(scriptKindName: protocol.ScriptKindName) {
 
 /** @internal */
 export function convertUserPreferences(preferences: protocol.UserPreferences): UserPreferences {
-    const { lazyConfiguredProjectsFromExternalProject, ...userPreferences } = preferences;
+    const { lazyConfiguredProjectsFromExternalProject: _, ...userPreferences } = preferences;
     return userPreferences;
 }
 
diff --git a/src/services/codefixes/fixAddMissingConstraint.ts b/src/services/codefixes/fixAddMissingConstraint.ts
index df99139e709c7..267cabc534e02 100644
--- a/src/services/codefixes/fixAddMissingConstraint.ts
+++ b/src/services/codefixes/fixAddMissingConstraint.ts
@@ -132,7 +132,7 @@ function addMissingConstraint(changes: textChanges.ChangeTracker, program: Progr
 }
 
 function tryGetConstraintFromDiagnosticMessage(messageText: string | DiagnosticMessageChain) {
-    const [_, constraint] = flattenDiagnosticMessageText(messageText, "\n", 0).match(/`extends (.*)`/) || [];
+    const [, constraint] = flattenDiagnosticMessageText(messageText, "\n", 0).match(/`extends (.*)`/) || [];
     return constraint;
 }
 
diff --git a/src/services/codefixes/fixNaNEquality.ts b/src/services/codefixes/fixNaNEquality.ts
index 53db6b1b8b8d6..bd64d8b82a70a 100644
--- a/src/services/codefixes/fixNaNEquality.ts
+++ b/src/services/codefixes/fixNaNEquality.ts
@@ -87,6 +87,6 @@ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, ar
 }
 
 function getSuggestion(messageText: string | DiagnosticMessageChain) {
-    const [_, suggestion] = flattenDiagnosticMessageText(messageText, "\n", 0).match(/'(.*)'/) || [];
+    const [, suggestion] = flattenDiagnosticMessageText(messageText, "\n", 0).match(/'(.*)'/) || [];
     return suggestion;
 }
diff --git a/src/services/refactors/moveToFile.ts b/src/services/refactors/moveToFile.ts
index 1c2a7a6e70265..80064ab148951 100644
--- a/src/services/refactors/moveToFile.ts
+++ b/src/services/refactors/moveToFile.ts
@@ -2,7 +2,6 @@ import {
     getModuleSpecifier,
 } from "../../compiler/moduleSpecifiers";
 import {
-    __String,
     AnyImportOrRequireStatement,
     append,
     ApplicableRefactorInfo,
diff --git a/src/tsconfig-base.json b/src/tsconfig-base.json
index 3b772f7d33f0f..498bd88917da4 100644
--- a/src/tsconfig-base.json
+++ b/src/tsconfig-base.json
@@ -21,10 +21,6 @@
         "useUnknownInCatchVariables": false,
         "noImplicitOverride": true,
 
-        "noUnusedLocals": true,
-        "noUnusedParameters": true,
-        "allowUnusedLabels": false,
-
         "skipLibCheck": true,
 
         "alwaysStrict": true,