Repository navigation
Description
π Search Terms
content mapper, built-in extension, dynamic LSP registration, duplicate inlay hints
β Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
Content Mappers currently target otherwise unsupported file extensions. It would be useful to let a mapper explicitly opt in to transforming files with built-in TypeScript/JavaScript extensions, such as .ts, while keeping the current restriction as the default.
The feature should also define how mapper applicability is scoped. For example, a mapper could explicitly claim selected .ts files, or be responsible for returning an unchanged, correctly mapped result when a file contains no syntax it handles. Simply registering .ts globally could otherwise cause ordinary TypeScript files to be transformed unexpectedly.
When a built-in extension is enabled, LSP capabilities already provided for JavaScript/TypeScript documents should not be registered again for the same documents. This is important for avoiding duplicate language-feature results, such as inlay hints.
π Motivating Example
Iβm building a tool that adds Result/Option-style early-return syntax to TypeScript. For example, it could transform:
function example() {
// If this is Err, return early; if it is Ok, unwrap the value.
const value = getResult().try;
return Ok(value);
}into virtual TypeScript such as:
import __$adapter$__ from "user-defined-adapter";
function example() {
return __$adapter$__.wrap(function* () {
const value = yield* __$adapter$__.unwrap(getResult());
return Ok(value);
});
}The tool can generate valid TypeScript and mappings back to the original source, but it cannot use Content Mapper for this workflow because the input file is .ts, a built-in extension.
π» Use Cases
- User-defined syntax sugar or macro-like transforms within ordinary .ts files.
- Result/Option workflows that unwrap values and propagate failures.
- Other source-level transforms that produce valid virtual TypeScript while retaining useful source mappings for language-service features.
The goal is not to add Result/Option semantics to TypeScript itself. It is to let users opt into their own transformation while keeping ordinary TypeScript behavior unchanged unless explicitly configured.
Activity
[-]Feature Request: Allow Content Mappers to opt in to transforming built-in TypeScript files[/-][+]Feature Request: Allow Content Mappers to opt in to transforming TypeScript/JavaScript files[/+][-]Feature Request: Allow Content Mappers to opt in to transforming TypeScript/JavaScript files[/-][+]Feature Request: Allow Content Mappers to opt-in to transforming files end in built-in extensions[/+]GoodbyeNJN commentedon Nov 1, 2026
Additional finding from a local experiment
To test whether my use case could work with .ts files, I temporarily bypassed the built-in-extension checks in
tsc/internal/contentmapper/contentmapper.goandpackages/vscode-typescript/src/contentMapperContributions.ts. This was only a local workaround to investigate the behavior; Iβm not proposing that the checks be removed as the fix.With
.tsregistered as a Content Mapper extension, each inlay hint appeared twice. The TypeScript client already provides inlay hints for TypeScript documents through its static JS/TS document selector, while the Content Mapper registration added a dynamic selector matching**/*.ts. The same document therefore matched both registrations.I confirmed the cause by locally filtering built-in JS/TS extensions out of the Content Mapperβs dynamic LSP registrations in
tsc/internal/lsp/server.go, leaving those documents to the existing JS/TS registration. With that change, the duplicate hints disappeared, while the Content Mapper still transformed the.tsfile.This seems like an LSP registration interaction that would need to be handled if Content Mappers are ever allowed to opt in to built-in extensions.
An opt-in mechanism for built-in extensions could also enable other use cases, such as the one discussed in #64917 and integrations with libraries like Effect-TS.
RyanCavanaugh commentedon Nov 2, 2026
This is very much an intentional exclusion. Content mappers are not here to provide language variants of TS, and we don't want the meaning of a TS file to be subject to unbounded re-interpretation depending on the tsconfig file. This also isn't composable in any meaningful way. I'd recommend doing something like
.etsif you want to make a specific new dialect of TS.loopingz commentedon Nov 2, 2026
It was possible in
7.1.0-dev.20260929.1opened the door to nice additional analysis, like validating additional SQL query based on the known schema. I understand the reasoning from Ryan Cavanaugh (@RyanCavanaugh) but would probably be nice to have the option/decision left to the user in my opinion.RyanCavanaugh commentedon Nov 3, 2026
Failing to block this doesn't mean it would just work. Configuration like
outputExtensionsfails to make any real sense if the input is a.tsfile, refactorings that assume they can apply to any .ts file stop working, it's unclear how to deal with multiple CMs all claiming to each handle.tsfiles (what order do they run in?), and so on.DetachHead commentedon Nov 5, 2026
i understand not wanting to support this specific use case, but being able to import json files
as consthas been a highly requested feature for years (see #32063 which has over 1300 upvotes) and content mappers seem like the perfect solution, if only we could use them on regular.jsonfiles.what if the built-in content mapper for
.jsonfiles was only applied whenresolveJsonModuleisfalse? that way, users can choose to override the built in.jsoncontent mapper with one that narrows the type.typescript-automation commentedon Nov 8, 2026
This issue has been marked as "Declined" and has seen no recent activity. It has been automatically closed for house-keeping purposes.