diff --git a/src/components/Comment.tsx b/src/components/Comment.tsx
index 61199d6a..3cb06157 100644
--- a/src/components/Comment.tsx
+++ b/src/components/Comment.tsx
@@ -20,6 +20,52 @@ export function hasComment(comment?: JSONOutput.Comment): boolean {
);
}
+interface SinceReflection {
+ comment?: JSONOutput.Comment;
+ inheritedFrom?: JSONOutput.ReferenceType;
+ sources?: JSONOutput.SourceReference[];
+}
+
+// Native runtime symbols: the TypeScript standard library (`lib.*.d.ts`) and
+// Node's built-in type definitions (`@types/node`). These have no meaningful
+// `@since` of their own — a `@since` on such a member would be copied noise — so
+// we always drop it. Other dependencies under `node_modules` are NOT native:
+// their `@since` tags (if any) reflect that package's real release history, so
+// inherited members keep them.
+const NATIVE_SOURCE = /(?:^|\/)node_modules\/(?:typescript\/lib\/|@types\/node\/)/;
+
+function isNativeReflection(reflection: SinceReflection): boolean {
+ const fileName = reflection.sources?.[0]?.fileName;
+
+ return !!fileName && NATIVE_SOURCE.test(fileName);
+}
+
+// Return the content of an `@since` tag explicitly present on the reflection.
+//
+// TypeDoc copies a base member's doc comment onto the members that inherit it,
+// so an inherited symbol naturally carries the `@since` it was given in its
+// base — we keep that, mirroring how every other inherited doc behaves
+// (including symbols inherited from other packages). The exception is members
+// inherited from a native runtime type (e.g. `Error`): those carry no real
+// version, so we drop it.
+export function getSinceContent(
+ reflection: SinceReflection | undefined,
+): JSONOutput.CommentDisplayPart[] | undefined {
+ const content = reflection?.comment?.blockTags?.find(
+ (blockTag) => blockTag.tag === '@since',
+ )?.content;
+
+ if (!content) {
+ return undefined;
+ }
+
+ if (reflection.inheritedFrom && isNativeReflection(reflection)) {
+ return undefined;
+ }
+
+ return content;
+}
+
export function displayPartsToMarkdown(parts: JSONOutput.CommentDisplayPart[]): string {
return parts
.map((part) => {
@@ -38,8 +84,7 @@ export function Comment({ comment, root, hideTags = [] }: CommentProps) {
}
// Hide custom tags.
- hideTags.push('@reference');
- hideTags.push('@since');
+ hideTags.push('@reference', '@since');
const blockTags =
comment.blockTags?.filter((tag) => {
diff --git a/src/components/MemberDeclaration.tsx b/src/components/MemberDeclaration.tsx
index d07eb3bd..4303c2a9 100644
--- a/src/components/MemberDeclaration.tsx
+++ b/src/components/MemberDeclaration.tsx
@@ -3,7 +3,7 @@
import { useMinimalLayout } from '../hooks/useMinimalLayout';
import { useRequiredReflection } from '../hooks/useReflection';
import { escapeMdx } from '../utils/helpers';
-import { Comment, displayPartsToMarkdown, hasComment } from './Comment';
+import { Comment, displayPartsToMarkdown, getSinceContent, hasComment } from './Comment';
import { DefaultValue } from './DefaultValue';
import { Icon } from './Icon';
import { Markdown } from './Markdown';
@@ -22,7 +22,7 @@ export function MemberDeclaration({ id }: MemberDeclarationProps) {
const minimal = useMinimalLayout();
const showTypes = reflection.typeParameters && reflection.typeParameters.length > 0;
const showDeclaration = !minimal && extractDeclarationFromType(reflection.type);
- const showSince = reflection.comment?.blockTags?.some((tag) => tag.tag === '@since');
+ const sinceContent = getSinceContent(reflection);
return (
<>
@@ -64,9 +64,9 @@ export function MemberDeclaration({ id }: MemberDeclarationProps) {
)}
- {showSince && (
+ {sinceContent && (
- tag.tag === '@since')?.content)} />
+
)}
diff --git a/src/components/MemberSignatureBody.tsx b/src/components/MemberSignatureBody.tsx
index 6e89cd40..a41253d0 100644
--- a/src/components/MemberSignatureBody.tsx
+++ b/src/components/MemberSignatureBody.tsx
@@ -7,15 +7,15 @@ import { usePluginData } from '@docusaurus/useGlobalData';
import { useMinimalLayout } from '../hooks/useMinimalLayout';
import type { TSDSignatureReflection } from '../types';
import { ApiDataContext } from './ApiDataContext';
-import { Comment, displayPartsToMarkdown, hasComment } from './Comment';
+import { Comment, displayPartsToMarkdown, getSinceContent, hasComment } from './Comment';
import { CommentBadges, isCommentWithModifiers } from './CommentBadges';
import { DefaultValue } from './DefaultValue';
import { Flags } from './Flags';
+import { Markdown } from './Markdown';
import { hasSources, MemberSources } from './MemberSources';
import { Parameter } from './Parameter';
import { extractDeclarationFromType, Type } from './Type';
import { TypeParameters } from './TypeParameters';
-import { Markdown } from './Markdown';
export function hasSigBody(
sig: TSDSignatureReflection | undefined,
@@ -63,9 +63,9 @@ export function MemberSignatureBody({ hideSources, sig }: MemberSignatureBodyPro
const showTypes = sig.typeParameter && sig.typeParameter.length > 0;
const showParams = !minimal && sig.parameters && sig.parameters.length > 0;
const showReturn = !minimal && sig.type;
- const showSince = sig.comment?.blockTags?.some((tag) => tag.tag === '@since');
const { reflections } = useContext(ApiDataContext);
+ const sinceContent = getSinceContent(sig);
const { isPython } = usePluginData('docusaurus-plugin-typedoc-api') as GlobalData;
if (isPython) {
@@ -218,10 +218,10 @@ export function MemberSignatureBody({ hideSources, sig }: MemberSignatureBodyPro
)}
{
- showSince && (
+ sinceContent && (
<>
- tag.tag === '@since')?.content)} />
+
>
)
diff --git a/src/components/Reflection.tsx b/src/components/Reflection.tsx
index 4150696e..69bfd918 100644
--- a/src/components/Reflection.tsx
+++ b/src/components/Reflection.tsx
@@ -3,11 +3,12 @@
import { useMemo } from 'react';
import type { TSDDeclarationReflection, TSDReflection, TSDSignatureReflection } from '../types';
import { createHierarchy } from '../utils/hierarchy';
-import { Comment, hasComment } from './Comment';
+import { Comment, displayPartsToMarkdown, getSinceContent, hasComment } from './Comment';
import { CommentBadges, isCommentWithModifiers } from './CommentBadges';
import { Hierarchy } from './Hierarchy';
import { Icon } from './Icon';
import { Index } from './Index';
+import { Markdown } from './Markdown';
import { Members } from './Members';
import { MemberSignatures } from './MemberSignatures';
import { Parameter } from './Parameter';
@@ -20,12 +21,24 @@ export interface ReflectionProps {
export function Reflection({ reflection }: ReflectionProps) {
const hierarchy = useMemo(() => createHierarchy(reflection), [reflection]);
+ // Callable top-level symbols (functions) render `@since` on their signatures
+ // below, so only surface it here for non-callable symbols (classes,
+ // interfaces, enums, type aliases, ...) where it would otherwise be missing.
+ const hasOwnSignatures =
+ 'signatures' in reflection && !!reflection.signatures && reflection.signatures.length > 0;
+ const sinceContent = hasOwnSignatures ? undefined : getSinceContent(reflection);
return (
<>
{isCommentWithModifiers(reflection.comment) && }
{hasComment(reflection.comment) && }
+ {sinceContent && (
+
+
+
+ )}
+
{'typeParameter' in reflection &&
reflection.typeParameter &&
reflection.typeParameter.length > 0 &&