diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 6b900302c96df..43f542270d9d8 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -1324,7 +1324,10 @@ function setExternalModuleIndicator(sourceFile: SourceFile) {
     sourceFile.externalModuleIndicator = isFileProbablyExternalModule(sourceFile);
 }
 
-export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
+export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile;
+/** @internal */
+export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes?: boolean, scriptKind?: ScriptKind, skipNonSemanticJSDoc?: boolean): SourceFile;
+export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes = false, scriptKind?: ScriptKind, skipNonSemanticJSDoc?: boolean): SourceFile {
     tracing?.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true);
     performance.mark("beforeParse");
     let result: SourceFile;
@@ -1336,14 +1339,14 @@ export function createSourceFile(fileName: string, sourceText: string, languageV
         impliedNodeFormat: format,
     } = typeof languageVersionOrOptions === "object" ? languageVersionOrOptions : ({ languageVersion: languageVersionOrOptions } as CreateSourceFileOptions);
     if (languageVersion === ScriptTarget.JSON) {
-        result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, ScriptKind.JSON, noop);
+        result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, ScriptKind.JSON, noop, skipNonSemanticJSDoc);
     }
     else {
         const setIndicator = format === undefined ? overrideSetExternalModuleIndicator : (file: SourceFile) => {
             file.impliedNodeFormat = format;
             return (overrideSetExternalModuleIndicator || setExternalModuleIndicator)(file);
         };
-        result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind, setIndicator);
+        result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind, setIndicator, skipNonSemanticJSDoc);
     }
     perfLogger?.logStopParseSourceFile();
 
@@ -1575,7 +1578,16 @@ namespace Parser {
     var parseErrorBeforeNextFinishedNode = false;
     /* eslint-enable no-var */
 
-    export function parseSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, syntaxCursor: IncrementalParser.SyntaxCursor | undefined, setParentNodes = false, scriptKind?: ScriptKind, setExternalModuleIndicatorOverride?: (file: SourceFile) => void): SourceFile {
+    export function parseSourceFile(
+        fileName: string,
+        sourceText: string,
+        languageVersion: ScriptTarget,
+        syntaxCursor: IncrementalParser.SyntaxCursor | undefined,
+        setParentNodes = false,
+        scriptKind?: ScriptKind,
+        setExternalModuleIndicatorOverride?: (file: SourceFile) => void,
+        skipNonSemanticJSDoc?: boolean,
+    ): SourceFile {
         scriptKind = ensureScriptKind(fileName, scriptKind);
         if (scriptKind === ScriptKind.JSON) {
             const result = parseJsonText(fileName, sourceText, languageVersion, syntaxCursor, setParentNodes);
@@ -1589,9 +1601,10 @@ namespace Parser {
             return result;
         }
 
-        initializeState(fileName, sourceText, languageVersion, syntaxCursor, scriptKind);
+        skipNonSemanticJSDoc = !!skipNonSemanticJSDoc && scriptKind !== ScriptKind.JS && scriptKind !== ScriptKind.JSX;
+        initializeState(fileName, sourceText, languageVersion, syntaxCursor, scriptKind, skipNonSemanticJSDoc);
 
-        const result = parseSourceFileWorker(languageVersion, setParentNodes, scriptKind, setExternalModuleIndicatorOverride || setExternalModuleIndicator);
+        const result = parseSourceFileWorker(languageVersion, setParentNodes, scriptKind, setExternalModuleIndicatorOverride || setExternalModuleIndicator, skipNonSemanticJSDoc);
 
         clearState();
 
@@ -1600,7 +1613,7 @@ namespace Parser {
 
     export function parseIsolatedEntityName(content: string, languageVersion: ScriptTarget): EntityName | undefined {
         // Choice of `isDeclarationFile` should be arbitrary
-        initializeState("", content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS);
+        initializeState("", content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS, /*skipNonSemanticJSDoc*/ false);
         // Prime the scanner.
         nextToken();
         const entityName = parseEntityName(/*allowReservedWords*/ true);
@@ -1610,7 +1623,7 @@ namespace Parser {
     }
 
     export function parseJsonText(fileName: string, sourceText: string, languageVersion: ScriptTarget = ScriptTarget.ES2015, syntaxCursor?: IncrementalParser.SyntaxCursor, setParentNodes = false): JsonSourceFile {
-        initializeState(fileName, sourceText, languageVersion, syntaxCursor, ScriptKind.JSON);
+        initializeState(fileName, sourceText, languageVersion, syntaxCursor, ScriptKind.JSON, /*skipNonSemanticJSDoc*/ false);
         sourceFlags = contextFlags;
 
         // Prime the scanner.
@@ -1698,7 +1711,7 @@ namespace Parser {
         return result;
     }
 
-    function initializeState(_fileName: string, _sourceText: string, _languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor | undefined, _scriptKind: ScriptKind) {
+    function initializeState(_fileName: string, _sourceText: string, _languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor | undefined, _scriptKind: ScriptKind, _skipNonSemanticJSDoc: boolean) {
         NodeConstructor = objectAllocator.getNodeConstructor();
         TokenConstructor = objectAllocator.getTokenConstructor();
         IdentifierConstructor = objectAllocator.getIdentifierConstructor();
@@ -1739,6 +1752,7 @@ namespace Parser {
         scanner.setOnError(scanError);
         scanner.setScriptTarget(languageVersion);
         scanner.setLanguageVariant(languageVariant);
+        scanner.setSkipNonSemanticJSDoc(_skipNonSemanticJSDoc);
     }
 
     function clearState() {
@@ -1746,6 +1760,7 @@ namespace Parser {
         scanner.clearCommentDirectives();
         scanner.setText("");
         scanner.setOnError(undefined);
+        scanner.setSkipNonSemanticJSDoc(false);
 
         // Clear any data.  We don't want to accidentally hold onto it for too long.
         sourceText = undefined!;
@@ -1762,7 +1777,7 @@ namespace Parser {
         topLevel = true;
     }
 
-    function parseSourceFileWorker(languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind, setExternalModuleIndicator: (file: SourceFile) => void): SourceFile {
+    function parseSourceFileWorker(languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind, setExternalModuleIndicator: (file: SourceFile) => void, skipNonSemanticJSDoc: boolean): SourceFile {
         const isDeclarationFile = isDeclarationFileName(fileName);
         if (isDeclarationFile) {
             contextFlags |= NodeFlags.Ambient;
@@ -1789,6 +1804,7 @@ namespace Parser {
         sourceFile.identifierCount = identifierCount;
         sourceFile.identifiers = identifiers;
         sourceFile.parseDiagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile);
+        sourceFile.skipNonSemanticJSDoc = skipNonSemanticJSDoc;
         if (jsDocDiagnostics) {
             sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile);
         }
@@ -8670,7 +8686,7 @@ namespace Parser {
 
     export namespace JSDocParser {
         export function parseJSDocTypeExpressionForTests(content: string, start: number | undefined, length: number | undefined): { jsDocTypeExpression: JSDocTypeExpression; diagnostics: Diagnostic[]; } | undefined {
-            initializeState("file.js", content, ScriptTarget.Latest, /*syntaxCursor*/ undefined, ScriptKind.JS);
+            initializeState("file.js", content, ScriptTarget.Latest, /*syntaxCursor*/ undefined, ScriptKind.JS, /*skipNonSemanticJSDoc*/ false);
             scanner.setText(content, start, length);
             currentToken = scanner.scan();
             const jsDocTypeExpression = parseJSDocTypeExpression();
@@ -8720,7 +8736,7 @@ namespace Parser {
         }
 
         export function parseIsolatedJSDocComment(content: string, start: number | undefined, length: number | undefined): { jsDoc: JSDoc; diagnostics: Diagnostic[]; } | undefined {
-            initializeState("", content, ScriptTarget.Latest, /*syntaxCursor*/ undefined, ScriptKind.JS);
+            initializeState("", content, ScriptTarget.Latest, /*syntaxCursor*/ undefined, ScriptKind.JS, /*skipNonSemanticJSDoc*/ false);
             const jsDoc = doInsideOfContext(NodeFlags.JSDoc, () => parseJSDocCommentWorker(start, length));
 
             const sourceFile = { languageVariant: LanguageVariant.Standard, text: content } as SourceFile;
@@ -9788,7 +9804,7 @@ namespace IncrementalParser {
         if (sourceFile.statements.length === 0) {
             // If we don't have any statements in the current source file, then there's no real
             // way to incrementally parse.  So just do a full parse instead.
-            return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, sourceFile.scriptKind, sourceFile.setExternalModuleIndicator);
+            return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, sourceFile.scriptKind, sourceFile.setExternalModuleIndicator, sourceFile.skipNonSemanticJSDoc);
         }
 
         // Make sure we're not trying to incrementally update a source file more than once.  Once
@@ -9851,7 +9867,7 @@ namespace IncrementalParser {
         // inconsistent tree.  Setting the parents on the new tree should be very fast.  We
         // will immediately bail out of walking any subtrees when we can see that their parents
         // are already correct.
-        const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind, sourceFile.setExternalModuleIndicator);
+        const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind, sourceFile.setExternalModuleIndicator, sourceFile.skipNonSemanticJSDoc);
         result.commentDirectives = getNewCommentDirectives(
             sourceFile.commentDirectives,
             result.commentDirectives,
diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index 13a3c37bc4126..113c956179188 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -402,6 +402,7 @@ export function createGetSourceFile(
     readFile: ProgramHost<any>["readFile"],
     getCompilerOptions: () => CompilerOptions,
     setParentNodes: boolean | undefined,
+    skipNonSemanticJSDocParsing: boolean | undefined,
 ): CompilerHost["getSourceFile"] {
     return (fileName, languageVersionOrOptions, onError) => {
         let text: string | undefined;
@@ -417,7 +418,7 @@ export function createGetSourceFile(
             }
             text = "";
         }
-        return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes) : undefined;
+        return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes, /*scriptKind*/ undefined, skipNonSemanticJSDocParsing) : undefined;
     };
 }
 
@@ -455,7 +456,12 @@ export function createWriteFileMeasuringIO(
 }
 
 /** @internal */
-export function createCompilerHostWorker(options: CompilerOptions, setParentNodes?: boolean, system: System = sys): CompilerHost {
+export function createCompilerHostWorker(
+    options: CompilerOptions,
+    setParentNodes?: boolean,
+    skipNonSemanticJSDocParsing?: boolean,
+    system: System = sys,
+): CompilerHost {
     const existingDirectories = new Map<string, boolean>();
     const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames);
     function directoryExists(directoryPath: string): boolean {
@@ -476,7 +482,7 @@ export function createCompilerHostWorker(options: CompilerOptions, setParentNode
     const newLine = getNewLineCharacter(options);
     const realpath = system.realpath && ((path: string) => system.realpath!(path));
     const compilerHost: CompilerHost = {
-        getSourceFile: createGetSourceFile(fileName => compilerHost.readFile(fileName), () => options, setParentNodes),
+        getSourceFile: createGetSourceFile(fileName => compilerHost.readFile(fileName), () => options, setParentNodes, skipNonSemanticJSDocParsing),
         getDefaultLibLocation,
         getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)),
         writeFile: createWriteFileMeasuringIO(
diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts
index 5a519dc79dcfb..e5cd8e6e5a5a2 100644
--- a/src/compiler/scanner.ts
+++ b/src/compiler/scanner.ts
@@ -114,6 +114,8 @@ export interface Scanner {
     // callback returns something truthy, then the scanner state is not rolled back.  The result
     // of invoking the callback is returned from this function.
     tryScan<T>(callback: () => T): T;
+    /** @internal */
+    setSkipNonSemanticJSDoc(skip: boolean): void;
 }
 
 /** @internal */
@@ -343,6 +345,11 @@ const commentDirectiveRegExSingleLine = /^\/\/\/?\s*@(ts-expect-error|ts-ignore)
  */
 const commentDirectiveRegExMultiLine = /^(?:\/|\*)*\s*@(ts-expect-error|ts-ignore)/;
 
+/**
+ * Test for whether a comment contains a JSDoc tag needed by the checker when run in tsc.
+ */
+const semanticJSDocTagRegEx = /@(?:see|link)/i;
+
 function lookupInUnicodeMap(code: number, map: readonly number[]): boolean {
     // Bail out quickly if it couldn't possibly be in the map.
     if (code < map[0]) {
@@ -1001,6 +1008,8 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
     var commentDirectives: CommentDirective[] | undefined;
     var inJSDocType = 0;
 
+    var skipNonSemanticJSDoc = false;
+
     setText(text, start, length);
 
     var scanner: Scanner = {
@@ -1052,6 +1061,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
         tryScan,
         lookAhead,
         scanRange,
+        setSkipNonSemanticJSDoc,
     };
     /* eslint-enable no-var */
 
@@ -1971,9 +1981,7 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
                     // Multi-line comment
                     if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) {
                         pos += 2;
-                        if (text.charCodeAt(pos) === CharacterCodes.asterisk && text.charCodeAt(pos + 1) !== CharacterCodes.slash) {
-                            tokenFlags |= TokenFlags.PrecedingJSDocComment;
-                        }
+                        const isJSDoc = text.charCodeAt(pos) === CharacterCodes.asterisk && text.charCodeAt(pos + 1) !== CharacterCodes.slash;
 
                         let commentClosed = false;
                         let lastLineStart = tokenStart;
@@ -1994,6 +2002,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
                             }
                         }
 
+                        if (isJSDoc && (!skipNonSemanticJSDoc || semanticJSDocTagRegEx.test(text.slice(fullStartPos, pos)))) {
+                            tokenFlags |= TokenFlags.PrecedingJSDocComment;
+                        }
+
                         commentDirectives = appendIfCommentDirective(commentDirectives, text.slice(lastLineStart, pos), commentDirectiveRegExMultiLine, lastLineStart);
 
                         if (!commentClosed) {
@@ -2775,6 +2787,10 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean
         languageVariant = variant;
     }
 
+    function setSkipNonSemanticJSDoc(skip: boolean) {
+        skipNonSemanticJSDoc = skip;
+    }
+
     function resetTokenState(position: number) {
         Debug.assert(position >= 0);
         pos = position;
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 85d8c8720c2bf..1b3d4c2a7cbef 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -4297,6 +4297,8 @@ export interface SourceFile extends Declaration, LocalsContainer {
 
     /** @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit;
     /** @internal */ endFlowNode?: FlowNode;
+
+    /** @internal */ skipNonSemanticJSDoc?: boolean;
 }
 
 /** @internal */
diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts
index fd4d4a8b6ca28..9d7991a2984fb 100644
--- a/src/compiler/watch.ts
+++ b/src/compiler/watch.ts
@@ -746,6 +746,7 @@ export function createCompilerHostFromProgramHost(host: ProgramHost<any>, getCom
             (fileName, encoding) => !encoding ? compilerHost.readFile(fileName) : host.readFile(fileName, encoding),
             getCompilerOptions,
             /*setParentNodes*/ undefined,
+            host.skipNonSemanticJSDocParsing,
         ),
         getDefaultLibLocation: maybeBind(host, host.getDefaultLibLocation),
         getDefaultLibFileName: options => host.getDefaultLibFileName(options),
diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts
index e963067868d66..6f432177ac8d5 100644
--- a/src/compiler/watchPublic.ts
+++ b/src/compiler/watchPublic.ts
@@ -118,8 +118,11 @@ export function readBuilderProgram(compilerOptions: CompilerOptions, host: ReadB
     return createBuilderProgramUsingProgramBuildInfo(buildInfo, buildInfoPath, host);
 }
 
-export function createIncrementalCompilerHost(options: CompilerOptions, system = sys): CompilerHost {
-    const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, system);
+export function createIncrementalCompilerHost(options: CompilerOptions, system?: System): CompilerHost;
+/** @internal */
+export function createIncrementalCompilerHost(options: CompilerOptions, system?: System, skipNonSemanticJSDocParsing?: boolean): CompilerHost;
+export function createIncrementalCompilerHost(options: CompilerOptions, system = sys, skipNonSemanticJSDocParsing?: boolean): CompilerHost {
+    const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, skipNonSemanticJSDocParsing, system);
     host.createHash = maybeBind(system, system.createHash);
     host.storeFilesChangingSignatureDuringEmit = system.storeFilesChangingSignatureDuringEmit;
     setGetSourceFileAsHashVersioned(host);
@@ -254,6 +257,13 @@ export interface ProgramHost<T extends BuilderProgram> {
      * Returns the module resolution cache used by a provided `resolveModuleNames` implementation so that any non-name module resolution operations (eg, package.json lookup) can reuse it
      */
     getModuleResolutionCache?(): ModuleResolutionCache | undefined;
+
+    /**
+     * True if it's safe for the parser to skip parsing non-semantic JSDoc tags.
+     *
+     * @internal
+     */
+    skipNonSemanticJSDocParsing?: boolean;
 }
 /**
  * Internal interface used to wire emit through same host
diff --git a/src/executeCommandLine/executeCommandLine.ts b/src/executeCommandLine/executeCommandLine.ts
index e7b2a1167a19d..99aa3b345ba45 100644
--- a/src/executeCommandLine/executeCommandLine.ts
+++ b/src/executeCommandLine/executeCommandLine.ts
@@ -789,6 +789,9 @@ function reportWatchModeWithoutSysSupport(sys: System, reportDiagnostic: Diagnos
     return false;
 }
 
+// This could be inlined everywhere, but this is convenient for debugging and patching.
+const skipNonSemanticJSDocParsing = true;
+
 function performBuild(
     sys: System,
     cb: ExecuteCommandLineCallbacks,
@@ -839,6 +842,7 @@ function performBuild(
             createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)),
             createWatchStatusReporter(sys, buildOptions),
         );
+        buildHost.skipNonSemanticJSDocParsing = skipNonSemanticJSDocParsing;
         const solutionPerformance = enableSolutionPerformance(sys, buildOptions);
         updateSolutionBuilderHost(sys, cb, buildHost, solutionPerformance);
         const onWatchStatusChange = buildHost.onWatchStatusChange;
@@ -868,6 +872,7 @@ function performBuild(
         createBuilderStatusReporter(sys, shouldBePretty(sys, buildOptions)),
         createReportErrorSummary(sys, buildOptions),
     );
+    buildHost.skipNonSemanticJSDocParsing = skipNonSemanticJSDocParsing;
     const solutionPerformance = enableSolutionPerformance(sys, buildOptions);
     updateSolutionBuilderHost(sys, cb, buildHost, solutionPerformance);
     const builder = createSolutionBuilder(buildHost, projects, buildOptions);
@@ -890,7 +895,7 @@ function performCompilation(
     config: ParsedCommandLine,
 ) {
     const { fileNames, options, projectReferences } = config;
-    const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, sys);
+    const host = createCompilerHostWorker(options, /*setParentNodes*/ undefined, skipNonSemanticJSDocParsing, sys);
     const currentDirectory = host.getCurrentDirectory();
     const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames());
     changeCompilerHostLikeToUseCache(host, fileName => toPath(fileName, currentDirectory, getCanonicalFileName));
@@ -923,7 +928,7 @@ function performIncrementalCompilation(
 ) {
     const { options, fileNames, projectReferences } = config;
     enableStatisticsAndTracing(sys, options, /*isBuildMode*/ false);
-    const host = createIncrementalCompilerHost(options, sys);
+    const host = createIncrementalCompilerHost(options, sys, skipNonSemanticJSDocParsing);
     const exitStatus = ts_performIncrementalCompilation({
         host,
         system: sys,
@@ -975,6 +980,7 @@ function updateWatchCompilationHost(
     cb: ExecuteCommandLineCallbacks,
     watchCompilerHost: WatchCompilerHost<EmitAndSemanticDiagnosticsBuilderProgram>,
 ) {
+    watchCompilerHost.skipNonSemanticJSDocParsing = skipNonSemanticJSDocParsing;
     updateCreateProgram(sys, watchCompilerHost, /*isBuildMode*/ false);
     const emitFilesUsingBuilder = watchCompilerHost.afterProgramCreate!; // TODO: GH#18217
     watchCompilerHost.afterProgramCreate = builderProgram => {
diff --git a/src/testRunner/tests.ts b/src/testRunner/tests.ts
index 1a3da111540f4..0d646f714d750 100644
--- a/src/testRunner/tests.ts
+++ b/src/testRunner/tests.ts
@@ -211,3 +211,4 @@ import "./unittests/tsserver/watchEnvironment";
 import "./unittests/debugDeprecation";
 import "./unittests/tsserver/inconsistentErrorInEditor";
 import "./unittests/tsserver/getMoveToRefactoringFileSuggestions";
+import "./unittests/skipNonSemanticJSDocParsing";
diff --git a/src/testRunner/unittests/skipNonSemanticJSDocParsing.ts b/src/testRunner/unittests/skipNonSemanticJSDocParsing.ts
new file mode 100644
index 0000000000000..94948e36e5855
--- /dev/null
+++ b/src/testRunner/unittests/skipNonSemanticJSDocParsing.ts
@@ -0,0 +1,60 @@
+import * as Harness from "../_namespaces/Harness";
+import * as ts from "../_namespaces/ts";
+import * as Utils from "../_namespaces/Utils";
+
+describe("unittests:: skipNonSemanticJSDocParsing", () => {
+    const Diff = require("diff");
+
+    function diffSourceFiles(name: string, content: string) {
+        it(name, () => {
+            const sourceFile = ts.createSourceFile("file.ts", content, ts.ScriptTarget.ESNext, /*setParentNodes*/ undefined, /*scriptKind*/ undefined);
+            assert.isTrue(sourceFile && sourceFile.parseDiagnostics.length === 0, "no errors issued");
+            const sourceFileSkipped = ts.createSourceFile("file.ts", content, ts.ScriptTarget.ESNext, /*setParentNodes*/ undefined, /*scriptKind*/ undefined, /*skipNonSemanticJSDoc*/ true);
+            assert.isTrue(sourceFileSkipped && sourceFileSkipped.parseDiagnostics.length === 0, "no errors issued");
+
+            const patch = Diff.createTwoFilesPatch("withJSDoc", "withoutJSDoc", Utils.sourceFileToJSON(sourceFile), Utils.sourceFileToJSON(sourceFileSkipped), "With JSDoc", "Without JSDoc");
+            Harness.Baseline.runBaseline("skipNonSemanticJSDocParsing/" + name + ".diff", patch);
+        });
+    }
+
+    diffSourceFiles(
+        "deprecated",
+        `
+/** @deprecated */
+function imDeprecated() {}
+imDeprecated()
+/**
+ * {@see imDeprecated}
+ * @deprecated
+ */
+function imDeprecated2() {}
+imDeprecated2()
+    `,
+    );
+
+    diffSourceFiles(
+        "see",
+        `
+/**
+ * @typedef {any} A
+ */
+
+/**
+ * @see {@link A}
+ * @see {@linkcode A}
+ * @see {@linkplain A}
+ */
+let foo;
+    `,
+    );
+
+    diffSourceFiles(
+        "link",
+        `
+import type { A } from "./a";
+
+/** {@link A} */
+export interface B {}
+    `,
+    );
+});
diff --git a/tests/baselines/reference/arrowFunctionJSDocAnnotation.symbols b/tests/baselines/reference/arrowFunctionJSDocAnnotation.symbols
new file mode 100644
index 0000000000000..436532de7fbc0
--- /dev/null
+++ b/tests/baselines/reference/arrowFunctionJSDocAnnotation.symbols
@@ -0,0 +1,28 @@
+//// [tests/cases/compiler/arrowFunctionJSDocAnnotation.ts] ////
+
+=== index.js ===
+/**
+ * @param {any} v
+ */
+function identity(v) {
+>identity : Symbol(identity, Decl(index.js, 0, 0))
+>v : Symbol(v, Decl(index.js, 3, 18))
+
+    return v;
+>v : Symbol(v, Decl(index.js, 3, 18))
+}
+
+const x = identity(
+>x : Symbol(x, Decl(index.js, 7, 5))
+>identity : Symbol(identity, Decl(index.js, 0, 0))
+
+    /**
+     * @param {number} param
+     * @returns {number=}
+     */
+    param => param
+>param : Symbol(param, Decl(index.js, 7, 19))
+>param : Symbol(param, Decl(index.js, 7, 19))
+
+);
+
diff --git a/tests/baselines/reference/arrowFunctionJSDocAnnotation.types b/tests/baselines/reference/arrowFunctionJSDocAnnotation.types
new file mode 100644
index 0000000000000..962a6582b5bb6
--- /dev/null
+++ b/tests/baselines/reference/arrowFunctionJSDocAnnotation.types
@@ -0,0 +1,30 @@
+//// [tests/cases/compiler/arrowFunctionJSDocAnnotation.ts] ////
+
+=== index.js ===
+/**
+ * @param {any} v
+ */
+function identity(v) {
+>identity : (v: any) => any
+>v : any
+
+    return v;
+>v : any
+}
+
+const x = identity(
+>x : any
+>identity(    /**     * @param {number} param     * @returns {number=}     */    param => param) : any
+>identity : (v: any) => any
+
+    /**
+     * @param {number} param
+     * @returns {number=}
+     */
+    param => param
+>param => param : (param: number) => number | undefined
+>param : number
+>param : number
+
+);
+
diff --git a/tests/baselines/reference/skipNonSemanticJSDocParsing/deprecated.diff b/tests/baselines/reference/skipNonSemanticJSDocParsing/deprecated.diff
new file mode 100644
index 0000000000000..12a169292e64b
--- /dev/null
+++ b/tests/baselines/reference/skipNonSemanticJSDocParsing/deprecated.diff
@@ -0,0 +1,67 @@
+===================================================================
+--- withJSDoc	With JSDoc
++++ withoutJSDoc	Without JSDoc
+@@ -8,9 +8,8 @@
+         "0": {
+             "kind": "FunctionDeclaration",
+             "pos": 0,
+             "end": 46,
+-            "flags": "Deprecated",
+             "modifierFlagsCache": 0,
+             "transformFlags": 4194304,
+             "name": {
+                 "kind": "Identifier",
+@@ -40,43 +39,9 @@
+                     "hasTrailingComma": false,
+                     "transformFlags": 0
+                 },
+                 "multiLine": false
+-            },
+-            "jsDoc": [
+-                {
+-                    "kind": "JSDoc",
+-                    "pos": 1,
+-                    "end": 19,
+-                    "flags": "JSDoc",
+-                    "modifierFlagsCache": 0,
+-                    "transformFlags": 0,
+-                    "tags": {
+-                        "0": {
+-                            "kind": "JSDocDeprecatedTag",
+-                            "pos": 5,
+-                            "end": 17,
+-                            "flags": "JSDoc",
+-                            "modifierFlagsCache": 0,
+-                            "transformFlags": 0,
+-                            "tagName": {
+-                                "kind": "Identifier",
+-                                "pos": 6,
+-                                "end": 16,
+-                                "flags": "JSDoc",
+-                                "modifierFlagsCache": 0,
+-                                "transformFlags": 0,
+-                                "escapedText": "deprecated"
+-                            }
+-                        },
+-                        "length": 1,
+-                        "pos": 5,
+-                        "end": 17,
+-                        "hasTrailingComma": false,
+-                        "transformFlags": 0
+-                    }
+-                }
+-            ]
++            }
+         },
+         "1": {
+             "kind": "ExpressionStatement",
+             "pos": 46,
+@@ -237,6 +202,6 @@
+     "typeReferenceDirectives": [],
+     "libReferenceDirectives": [],
+     "amdDependencies": [],
+     "identifiers": {},
+-    "skipNonSemanticJSDoc": false
++    "skipNonSemanticJSDoc": true
+ }
+\ No newline at end of file
diff --git a/tests/baselines/reference/skipNonSemanticJSDocParsing/link.diff b/tests/baselines/reference/skipNonSemanticJSDocParsing/link.diff
new file mode 100644
index 0000000000000..e7b3e2a532791
--- /dev/null
+++ b/tests/baselines/reference/skipNonSemanticJSDocParsing/link.diff
@@ -0,0 +1,12 @@
+===================================================================
+--- withJSDoc	With JSDoc
++++ withoutJSDoc	Without JSDoc
+@@ -220,6 +220,6 @@
+     "typeReferenceDirectives": [],
+     "libReferenceDirectives": [],
+     "amdDependencies": [],
+     "identifiers": {},
+-    "skipNonSemanticJSDoc": false
++    "skipNonSemanticJSDoc": true
+ }
+\ No newline at end of file
diff --git a/tests/baselines/reference/skipNonSemanticJSDocParsing/see.diff b/tests/baselines/reference/skipNonSemanticJSDocParsing/see.diff
new file mode 100644
index 0000000000000..cbad93fc6bd33
--- /dev/null
+++ b/tests/baselines/reference/skipNonSemanticJSDocParsing/see.diff
@@ -0,0 +1,12 @@
+===================================================================
+--- withJSDoc	With JSDoc
++++ withoutJSDoc	Without JSDoc
+@@ -307,6 +307,6 @@
+     "typeReferenceDirectives": [],
+     "libReferenceDirectives": [],
+     "amdDependencies": [],
+     "identifiers": {},
+-    "skipNonSemanticJSDoc": false
++    "skipNonSemanticJSDoc": true
+ }
+\ No newline at end of file
diff --git a/tests/cases/compiler/arrowFunctionJSDocAnnotation.ts b/tests/cases/compiler/arrowFunctionJSDocAnnotation.ts
new file mode 100644
index 0000000000000..47f6c2b56574b
--- /dev/null
+++ b/tests/cases/compiler/arrowFunctionJSDocAnnotation.ts
@@ -0,0 +1,21 @@
+// @checkJs: true
+// @allowJs: true
+// @noEmit: true
+// @strict: true
+
+// @filename: index.js
+
+/**
+ * @param {any} v
+ */
+function identity(v) {
+    return v;
+}
+
+const x = identity(
+    /**
+     * @param {number} param
+     * @returns {number=}
+     */
+    param => param
+);