fix(typescript) highlight generic function declarations - #4551
Open
Kernel-Hunter wants to merge 1 commit into
Open
Kernel-Hunter wants to merge 1 commit into
Kernel-Hunter wants to merge 1 commit into
Conversation
The function declaration rule comes from the JavaScript grammar and expects `(` right after the name. With type parameters in between (`function identity<T>(arg: T)`) it never matched, so the name got no title.function scope and the parameters were not scoped as params. Add a TypeScript-only variant that matches when the name is followed by `<`, plus a type parameter mode that handles nested `<...>` and skips the `>` of `=>` inside function types. The parameter list after it is picked up by the existing params rule. Assisted-by: Claude Opus 5.5
nexoracontrol-ops
approved these changes
Oct 3, 2026
kgeminicdev
reviewed
Oct 5, 2026
kgeminicdev
left a comment
There was a problem hiding this comment.
Thanks, this fixes #4550 nicely. Nested generics, defaults, => in function types and declare function all highlight correctly, and plain functions are unchanged. One regression with string literal types in the type parameters, details and a tested fix inline.
| // `=>` inside a function type must not close the list | ||
| { match: /=>/ }, | ||
| tsLanguage.exports.CLASS_REFERENCE, | ||
| 'self' |
There was a problem hiding this comment.
A > inside a string literal type closes the type parameter list early, since strings aren't in contains here. On main these highlight correctly; with this change:
function f<T extends "a>b">(x: T) {}: the string is cut at>, and">(x: T) {}is highlighted as a string.function f<T extends `${number}>`>(x: T) {}: the template literal is cut, and since template strings span lines, everything after it is highlighted as a string.
Adding the string modes fixed both cases for me, and generic-functions still produces the same output:
contains: [
{ match: /=>/ },
tsLanguage.exports.CLASS_REFERENCE,
hljs.APOS_STRING_MODE,
hljs.QUOTE_STRING_MODE,
tsLanguage.contains.find(m => m.begin === '`'), // or export TEMPLATE_STRING alongside CLASS_REFERENCE
'self'
]Might be worth adding both lines to the markup test.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4550
Changes
The function declaration rule comes from the JavaScript grammar and only matches when
(directly follows the name. With type parameters in between, as infunction identity<T>(arg: T), nothing matched: the name got notitle.functionand the arguments weren't scoped asparams.This adds a TypeScript-only variant for
function name<, and a mode for the type parameter list that handles nested<...>(T extends Record<string, unknown>) and ignores the>of=>inside function types (F extends (x: number) => void). After the list closes, the existing params rule picks up the argument list. Output for non-generic functions and plain JS is unchanged.Not covered:
function* gen<T>()and anonymousfunction<T>(). I can do those separately if you want them.Checklist
test/markup/typescript/generic-functions)CHANGES.mdAssisted-by: Claude Opus 5.5