diff --git a/src/generators/metadata/constants.mjs b/src/generators/metadata/constants.mjs index 14d22757..12ed7fd5 100644 --- a/src/generators/metadata/constants.mjs +++ b/src/generators/metadata/constants.mjs @@ -1,6 +1,7 @@ // These openers/closers are used to determine if a type string is well-formed export const TYPE_OPENERS = new Set(['<', '(', '{', '[']); export const TYPE_CLOSERS = new Set(['>', ')', '}', ']']); +export const PREFIXES = ['typeof ', 'keyof ', 'readonly ', 'unique ']; // On "About this Documentation", we define the stability indices, and thus // we don't need to check it for stability references diff --git a/src/generators/metadata/utils/__tests__/transformers.test.mjs b/src/generators/metadata/utils/__tests__/transformers.test.mjs index 7405ddcb..fd5a8cd8 100644 --- a/src/generators/metadata/utils/__tests__/transformers.test.mjs +++ b/src/generators/metadata/utils/__tests__/transformers.test.mjs @@ -93,6 +93,24 @@ describe('transformTypeToReferenceLink', () => { ); }); + it('should correctly extract TS prefix operators and link the underlying type', () => { + strictEqual( + transformTypeToReferenceLink('typeof Compiler', { + Compiler: '/api/Compiler', + }), + 'typeof [``](/api/Compiler)' + ); + }); + + it('should not incorrectly strip prefixes if they are part of the type name (word boundary)', () => { + strictEqual( + transformTypeToReferenceLink('typeofSomething', { + typeofSomething: '/api/typeofSomething', + }), + '[``](/api/typeofSomething)' + ); + }); + it('should handle extreme nested combinations of functions, arrays, generics, unions, and intersections', () => { const input = '(str: string[]) => Promise, Map>'; diff --git a/src/generators/metadata/utils/typeParser.mjs b/src/generators/metadata/utils/typeParser.mjs index 5d977379..e2fcd10b 100644 --- a/src/generators/metadata/utils/typeParser.mjs +++ b/src/generators/metadata/utils/typeParser.mjs @@ -1,4 +1,4 @@ -import { TYPE_OPENERS, TYPE_CLOSERS } from '../constants.mjs'; +import { TYPE_OPENERS, TYPE_CLOSERS, PREFIXES } from '../constants.mjs'; /** True when the `>` at `i` is the tail of `=>` and shouldn't pop depth. */ const isArrowTail = (str, i) => str[i] === '>' && str[i - 1] === '='; @@ -206,6 +206,13 @@ export const parseType = (typeString, transformType) => { return parts.map(p => resolveOr(p, transformType)).join(joiner); } + for (const prefix of PREFIXES) { + if (trimmed.startsWith(prefix)) { + const rest = trimmed.slice(prefix.length).trim(); + return `${prefix.trim()} ${resolveOr(rest, transformType)}`; + } + } + // Strip a trailing `[]` for now; reapply on the way out. const isArray = trimmed.endsWith('[]'); const core = isArray ? trimmed.slice(0, -2).trim() : trimmed;