diff --git a/.chronus/changes/fix-symbol-decorator-state-display-2026-02-02-13-19-46.md b/.chronus/changes/fix-symbol-decorator-state-display-2026-02-02-13-19-46.md new file mode 100644 index 00000000000..18c593935c8 --- /dev/null +++ b/.chronus/changes/fix-symbol-decorator-state-display-2026-02-02-13-19-46.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/html-program-viewer" +--- + +Fix type graph viewer to display Symbol-keyed decorator state diff --git a/packages/html-program-viewer/src/react/js-inspector/object-inspector.tsx b/packages/html-program-viewer/src/react/js-inspector/object-inspector.tsx index b5bf0f4af58..e0b4817fea9 100644 --- a/packages/html-program-viewer/src/react/js-inspector/object-inspector.tsx +++ b/packages/html-program-viewer/src/react/js-inspector/object-inspector.tsx @@ -33,19 +33,25 @@ const createIterator = (showNonenumerable?: boolean, sortObjectKeys?: boolean) = i++; } } else { - const keys = Object.getOwnPropertyNames(data); - if (sortObjectKeys === true && !dataIsArray) { + // Get all property keys (both string and Symbol) + const stringKeys = Object.getOwnPropertyNames(data); + const symbolKeys = Object.getOwnPropertySymbols(data); + const allKeys: (string | symbol)[] = [...stringKeys, ...symbolKeys]; + + if (sortObjectKeys && !dataIsArray) { // Array keys should not be sorted in alphabetical order - keys.sort(); - } else if (typeof sortObjectKeys === "function") { - keys.sort(sortObjectKeys); + allKeys.sort((a, b) => { + const aStr = typeof a === "string" ? a : a.toString(); + const bStr = typeof b === "string" ? b : b.toString(); + return aStr.localeCompare(bStr); + }); } - for (const propertyName of keys) { - if (propertyIsEnumerable.call(data, propertyName)) { - const propertyValue = getPropertyValue(data, propertyName); + for (const key of allKeys) { + if (propertyIsEnumerable.call(data, key)) { + const propertyValue = getPropertyValue(data, key); yield { - name: propertyName || `""`, + name: typeof key === "string" ? key || `""` : key.toString(), data: propertyValue, }; } else if (showNonenumerable) { @@ -54,14 +60,14 @@ const createIterator = (showNonenumerable?: boolean, sortObjectKeys?: boolean) = // http://stackoverflow.com/questions/31921189/caller-and-arguments-are-restricted-function-properties-and-cannot-be-access let propertyValue; try { - propertyValue = getPropertyValue(data, propertyName); + propertyValue = getPropertyValue(data, key); } catch (e) { // console.warn(e) } if (propertyValue !== undefined) { yield { - name: propertyName, + name: typeof key === "string" ? key || `""` : key.toString(), data: propertyValue, isNonenumerable: true, }; diff --git a/packages/html-program-viewer/src/react/js-inspector/object-preview.tsx b/packages/html-program-viewer/src/react/js-inspector/object-preview.tsx index f5e0a98b6fb..6e2ca37c124 100644 --- a/packages/html-program-viewer/src/react/js-inspector/object-preview.tsx +++ b/packages/html-program-viewer/src/react/js-inspector/object-preview.tsx @@ -4,7 +4,7 @@ import { JsValue } from "./js-value/js-value.js"; import { ObjectName } from "./object-name.js"; import style from "./object-inspector.module.css"; -import { hasOwnProperty } from "./utils/object-prototype.js"; +import { hasOwnProperty, propertyIsEnumerable } from "./utils/object-prototype.js"; import { getPropertyValue } from "./utils/property-utils.js"; /* intersperse arr with separator */ @@ -54,27 +54,36 @@ export const ObjectPreview: FC = ({ data }) => { } else { const maxProperties = OBJECT_MAX_PROPERTIES; const propertyNodes: ReactNode[] = []; - for (const propertyName in object) { - if (hasOwnProperty.call(object, propertyName)) { - let ellipsis; - if ( - propertyNodes.length === maxProperties - 1 && - Object.keys(object).length > maxProperties - ) { - ellipsis = ; - } - - const propertyValue = getPropertyValue(object, propertyName); - propertyNodes.push( - - - :  - - {ellipsis} - , - ); - if (ellipsis) break; + + // Get all property keys (both string and Symbol), filtering for enumerable ones + const stringKeys = Object.keys(object); // Object.keys only returns enumerable string properties + const symbolKeys = Object.getOwnPropertySymbols(object).filter(sym => + propertyIsEnumerable.call(object, sym) + ); + const allKeys: (string | symbol)[] = [...stringKeys, ...symbolKeys]; + const totalProperties = allKeys.length; + + for (let i = 0; i < allKeys.length; i++) { + const key = allKeys[i]; + let ellipsis; + if ( + propertyNodes.length === maxProperties - 1 && + totalProperties > maxProperties + ) { + ellipsis = ; } + + const propertyValue = getPropertyValue(object, key); + const displayName = typeof key === "string" ? key || `""` : key.toString(); + propertyNodes.push( + + + :  + + {ellipsis} + , + ); + if (ellipsis) break; } const objectConstructorName = object.constructor ? object.constructor.name : "Object"; diff --git a/packages/html-program-viewer/src/react/js-inspector/utils/property-utils.tsx b/packages/html-program-viewer/src/react/js-inspector/utils/property-utils.tsx index 614e3975b2c..b87dc15df2a 100644 --- a/packages/html-program-viewer/src/react/js-inspector/utils/property-utils.tsx +++ b/packages/html-program-viewer/src/react/js-inspector/utils/property-utils.tsx @@ -1,4 +1,4 @@ -export function getPropertyValue(object: any, propertyName: string) { +export function getPropertyValue(object: any, propertyName: string | symbol) { const propertyDescriptor = Object.getOwnPropertyDescriptor(object, propertyName); if (propertyDescriptor?.get) { try {