Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

Develop a TypeScript language service plugin: Make RTK Query's "Go to Definition" smarter

Introduction Have you ever encountered this frustration when developing with Redux Toolkit Query (RTK Query)? When you want to check the implementation of an API endpoint, pressing F12 (Go to Definition) takes you to the type definition…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!




Introduction



Have you ever encountered this frustration when developing with Redux Toolkit Query (RTK Query)?



When you want to check the implementation of an API endpoint, pressing F12 (Go to Definition) takes you to the type definition file instead of the actual business logic. You have to manually search for the endpoint name to find its definition in createApi.



This is a common problem because RTK Query hook names (like useGetUserQuery) are dynamically generated, and TypeScript cannot establish a static mapping from hook calls to endpoint definitions.



Today, I'll show you how to develop a TypeScript Language Service Plugin to solve this problem, allowing developers to jump directly to RTK Query endpoint definitions with a single click.









Problem Background






How RTK Query Works



RTK Query creates API slices through createApi:




export const userApi = createApi({
reducerPath: 'userApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getUser: builder.query<User, string>({
query: (id) => `/users/${id}`,
}),
updateUser: builder.mutation<User, Partial<User>>({
query: (body) => ({
url: '/users',
method: 'POST',
body,
}),
}),
}),
})

// Auto-generated hooks
export const { useGetUserQuery, useUpdateUserMutation } = userApi









Pain Points





  1. Hook names are dynamically derived: getUser → useGetUserQuery


  2. TypeScript only sees types: IDE's "Go to Definition" can only point to type gymnastics generated type definitions


  3. Broken developer experience: Developers need to manually search for endpoint names, interrupting the coding flow









Solution: TypeScript Language Service Plugin






What is a Language Service Plugin?



TypeScript Language Service Plugin is an extension mechanism that allows us to intercept and customize various TypeScript language service operations, including:




  • Go to Definition

  • Auto Completion

  • Hover Information

  • Code Refactoring






Core Idea



Our plugin needs to accomplish the following:





  1. Identify RTK Query Hooks: Recognize hooks like use{Endpoint}Query, use{Endpoint}Mutation through naming conventions


  2. Parse AST: Find the API instance the hook belongs to


  3. Locate Endpoint: Find the corresponding endpoint definition from the API instance's endpoints property


  4. Return Definition Location: Point the jump target to the endpoint definition









Implementation Details






1. Project Structure






rtk-to-endpoints/
├── src/
│ ├── index.ts # Plugin entry
│ └── utils.ts # Core logic
├── package.json
└── tsconfig.json









2. Plugin Entry (index.ts)






import tslib from "typescript/lib/tsserverlibrary";
import { getDefinitionAndBoundSpan } from "./utils.js";

function init(modules: { typescript: typeof tslib }) {
const ts = modules.typescript;

function create(info: tslib.server.PluginCreateInfo) {
const logger = info.project.projectService.logger;

log("✅ Plugin initialized");

const proxy: tslib.LanguageService = Object.create(info.languageService);

// Intercept "Go to Definition" request
proxy.getDefinitionAndBoundSpan = (
fileName: string,
position: number
): tslib.DefinitionInfoAndBoundSpan | undefined => {
const program = info.languageService.getProgram();

// Try our custom jump logic
const definitionInfo = getDefinitionAndBoundSpan(
fileName, position, ts, program
);

// If RTK Query hook is matched, return custom result
// Otherwise, fall back to default behavior
return definitionInfo ||
info.languageService.getDefinitionAndBoundSpan(fileName, position);
};

return proxy;
}

return { create };
}

export = init;









3. Core Logic (utils.ts)






3.1 Recognizing Hook Naming Patterns



RTK Query generated hooks follow fixed naming conventions:




const HOOK_PREFIXES = ["useLazy", "use"] as const;
const HOOK_SUFFIXES = [
"InfiniteQueryState",
"InfiniteQuery",
"QueryState",
"Mutation",
"Query",
] as const;

// Extract endpoint name from hook name
export function extractEndpointName(hookName: string) {
for (const prefix of HOOK_PREFIXES) {
if (hookName.startsWith(prefix)) {
const rest = hookName.slice(prefix.length);
for (const suffix of HOOK_SUFFIXES) {
if (rest.endsWith(suffix)) {
const endpointName = rest.slice(0, rest.length - suffix.length);
if (endpointName) {
// Lowercase first letter: GetUser → getUser
return endpointName[0].toLowerCase() + endpointName.slice(1);
}
}
}
}
}
}









3.2 AST Node Lookup



Use binary search to quickly locate the node at cursor position in the AST:




export function getIdentifierNodeAt(
sourceFile: tslib.SourceFile,
pos: number,
): tslib.Node | undefined {
let current: tslib.Node = sourceFile;

while (true) {
const children = current.getChildren(sourceFile);
let left = 0;
let right = children.length - 1;
let targetChild: tslib.Node | undefined;

// Binary search for child node covering the specified position
while (left <= right) {
const mid = (left + right) >>> 1;
const child = children[mid];
if (pos < child.pos) {
right = mid - 1;
} else if (pos >= child.end) {
left = mid + 1;
} else {
targetChild = child;
break;
}
}

if (!targetChild) break;
current = targetChild;
}

return current;
}









3.3 Finding API Instance



Support two common API usage patterns:




export function findApi(node: tslib.Node, ts: typeof tslib) {
const parent = node.parent;

// Pattern 1: Destructuring assignment
// const { useGetUsersQuery } = userApi
if (ts.isBindingElement(parent)) {
const expressionNode = parent.parent?.parent;
if (!ts.isVariableDeclaration(expressionNode)) return;
const apiNode = expressionNode.getChildAt(
expressionNode.getChildCount() - 1
);
if (!apiNode || !ts.isIdentifier(apiNode)) return;
return apiNode;

// Pattern 2: Property access
// userApi.useGetProductsQuery()
} else if (parent && ts.isPropertyAccessExpression(parent)) {
return parent.getChildAt(parent.getChildCount() - 3);
}
}









3.4 Locating Endpoint Definition



Use TypeScript's type checker to find the target endpoint from the API instance's endpoints property:




export function findEndpoint(
apiNode: tslib.Node,
endpointName: string,
checker: tslib.TypeChecker
) {
// Get the type of API instance
const apiType = checker.getTypeAtLocation(apiNode);

// Get endpoints property
const endpointsSymbol = apiType.getProperty('endpoints');
if (!endpointsSymbol) return;

// Get the type of endpoints
const endpointsType = checker.getTypeOfSymbol(endpointsSymbol);

// Find specific endpoint
const endpointsPropertySymbol = endpointsType.getProperty(endpointName);
return endpointsPropertySymbol;
}









3.5 Assembling Definition Information






export function getDefinitionAndBoundSpan(
fileName: string,
position: number,
ts: typeof tslib,
program?: tslib.Program
) {
const sf = program!.getSourceFile(fileName);
const checker = program!.getTypeChecker();
if (!sf || !program || !checker) return;

// 1. Find identifier node at cursor position
const identNode = getIdentifierNodeAt(sf, position);
if (!identNode || !ts.isIdentifier(identNode)) return;

// 2. Extract endpoint name
const endpointName = extractEndpointName(identNode.getText());
if (!endpointName) return;

// 3. Find API instance
const apiNode = findApi(identNode, ts);
if (!apiNode) return;

// 4. Find endpoint definition
const endpointSymbol = findEndpoint(apiNode, endpointName, checker);
if (!endpointSymbol?.declarations?.length) return;

// 5. Assemble definition information
const definitions = endpointSymbol.declarations.map((node): tslib.DefinitionInfo => {
return {
fileName: node.getSourceFile().fileName,
kind: ts.ScriptElementKind.memberFunctionElement,
name: endpointSymbol.getName(),
containerKind: ts.ScriptElementKind.classElement,
containerName: "endpoints",
textSpan: {
start: node.getStart(),
length: node.getWidth(),
},
};
});

return {
definitions,
textSpan: {
start: identNode.getStart(sf),
length: identNode.getWidth(sf),
},
};
}












Usage






1. Install the Plugin






npm install --save-dev rtk-to-endpoints









2. Configure tsconfig.json






{
"compilerOptions": {
"plugins": [
{
"name": "rtk-to-endpoints"
}
]
}
}









3. Configure VSCode



Since VSCode's built-in TypeScript cannot read npm packages from the project, you need to set VSCode to use the workspace TypeScript version:





  1. Ctrl+Shift+P → Type "TypeScript: Select TypeScript Version"

  2. Select "Use Workspace Version"

  3. Reload window (Developer: Reload Window)









Demo



After configuration, when you use "Go to Definition" on any RTK Query hook:




// Click useGetUserQuery to jump directly to getUser endpoint definition
const { data } = userApi.useGetUserQuery(userId);






Before:




  • Points to type definition file (no practical business value)



After:




  • Directly locates the getUser endpoint definition in createApi









Technical Summary






1. TypeScript Language Service Architecture






┌─────────────────────────────────────────┐
│ VSCode / IDE │
└─────────────┬───────────────────────────┘
│ LSP Protocol
┌─────────────▼───────────────────────────┐
│ TypeScript Language Server │
└─────────────┬───────────────────────────┘
│
┌─────────────▼───────────────────────────┐
│ TypeScript Language Service │
│ ┌─────────────────────────────────┐ │
│ │ rtk-to-endpoints Plugin │ │
│ │ (Intercept getDefinition) │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘









2. Key Technical Points




























Technical Point Description
AST Traversal Use binary search to efficiently locate nodes
Type Checker Use TypeChecker to parse type information
Proxy Pattern Wrap original Language Service, preserve default behavior
Naming Resolution Identify hook types through string pattern matching








Extension Ideas



The implementation of this plugin can be extended to other similar scenarios:





  1. Vue Composition API: Jump from useXxx to composable definitions


  2. React Hooks: Enhance custom hook navigation experience









Conclusion



TypeScript Language Service Plugin is a powerful tool that can significantly improve the developer experience. By understanding TypeScript's compiler API and language service architecture, we can build smarter IDE support for specific frameworks and libraries.



I hope this article helps you understand how Language Service Plugins work and inspires you to develop similar tools for your own projects.









References








If this article helped you, please like, bookmark, and share!



Feel free to leave comments if you have any questions or suggestions.

1. Sofort-Triage & Abwehrmaßnahmen

SOC Incident Playbook: Remote Code Execution (RCE) Defense
1 Warnungen
title: Detect Exploitation - Develop a TypeScript language service plugin: Make RTK Query's "Go to Definition" smarter
id: 3575b7d2-592f-4c6d-ac6b-8ee4187459bf
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-27
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
Syntax validiert (0 Fehler)
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-27"
        description = "YARA Signature for "
    strings:
        $str = "Develop a TypeScript language " ascii wide
    condition:
        any of them
}
Syntax validiert (0 Fehler)
index=security sourcetype IN ("cisco:asa", "pan:traffic", "zeek_conn", "suricata", "WinEventLog:Security")
("Develop a TypeScript language service pl")
| stats count earliest(_time) as first_seen latest(_time) as last_seen by src_ip, dest_ip, dest_host, signature
| eval first_seen=strftime(first_seen, "%Y-%m-%d %H:%M:%S"), last_seen=strftime(last_seen, "%Y-%m-%d %H:%M:%S")
| sort - count
Syntax validiert (0 Fehler)
message: "*Develop a TypeScript language service pl*"
Syntax validiert (0 Fehler)
CommonSecurityLog
| where Message has "Develop a TypeScript language service pl"
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, DestinationIP, DestinationPort, Activity
| extend DetectionRule = "iShareStuff-CTI-Compiled"
| sort by EventCount desc

2. Cyber Threat Intelligence & Forensik

🎯
MITRE ATT&CK Matrix Navigator 14 Taktiken
Reconnaissance
-
Resource Development
-
Initial Access
Execution
Persistence
-
Privilege Escalation
Defense Evasion
Credential Access
-
Discovery
-
Lateral Movement
-
Collection
-
Command and Control
Exfiltration
-
Impact
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Analyse für identifizierte Bedrohung auf Basis von Live-CTI (ENISA EUVD): CVSS 0.0 · EPSS 0.0% · CISA KEV: nein. Handlungsableitung aus den verlinkten Hersteller-Quellen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

⚡ Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Develop a TypeScript language service plugin: Make RTK Query's "Go to Definition" smarter

Thematisch verwandte Begriffe: Develop, TypeScript, language, service · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-100739 | A vulnerability was detected in mathurvishal CloudClassroom-PHP-Project…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag