Repository navigation
[Bug]: tsrx-tsc drops TS1360 when a satisfies check fails #243
Description
Issue Category
Compilation Bug (command-line type checking).
Bug Description
tsrx-tsc --noEmit exits 0 when a satisfies check fails. TypeScript reports TS1360 (Type 'X' does not satisfy the expected type 'Y') on the satisfies keyword itself, and no mapping covers that keyword. This happens for any operand: a literal, an identifier, a call, this, a parenthesized expression, or a chained as ... satisfies. It also happens in a function body and a component template body.
Reproduction Steps
At 62ef14a7, run pnpm install --frozen-lockfile --ignore-scripts, then create repro/ at the repository root.
repro/package.json:
{"type":"module"}repro/tsconfig.json:
{
"tsrx": { "compiler": "@tsrx/preact" },
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
"target": "ES2022",
"jsx": "preserve",
"strict": true,
"skipLibCheck": true,
"types": []
},
"include": ["case.tsrx"]
}repro/case.tsrx:
declare const count: number;
declare function load(): number;
export const a = 1 satisfies string;
export const b = count satisfies string;
export const c = load() satisfies string;
export function read() {
return count as unknown as number satisfies string;
}Run from the repository root:
node packages/typescript-plugin/src/tsc.js --noEmit --pretty false -p repro/tsconfig.jsonExpected (what tsc reports for the same source saved as case.ts):
repro/case.ts(3,20): error TS1360: Type 'number' does not satisfy the expected type 'string'.
repro/case.ts(4,24): error TS1360: Type 'number' does not satisfy the expected type 'string'.
repro/case.ts(5,25): error TS1360: Type 'number' does not satisfy the expected type 'string'.
repro/case.ts(7,36): error TS1360: Type 'number' does not satisfy the expected type 'string'.
Actual: exit 0 with no output.
In a component template body (export function App() @{ const v = 1 satisfies string; <div>{v}</div> }), TS1360 is also dropped. It reports only inside a JSX expression container (<div>{1 satisfies string}</div>), because the container's mapping covers the keyword.
Cause
The TSAsExpression / TSSatisfiesExpression case in packages/tsrx/src/transform/segments.js visits the expression and the type annotation but maps nothing between them. esrap prints the keyword with context.write(' satisfies ') and no location, in 2.3.2 and in 2.3.11. None of the open esrap PRs (sveltejs/esrap#212, #213, #214, #215) change that. With esrap 2.3.11 linked in, the repro still exits 0.
as doesn't have this problem. TypeScript reports TS2352 on the whole "s" as number, and Volar maps its start and end through two different mappings (the operand and the type). TS1360 starts and ends inside the unmapped keyword, so nothing can map it.
Fix Direction
Two options; I haven't tried either:
- Map the keyword. The source span can come from the lexer:
parse/index.jsalready collectsfunctionandasyncthroughonToken(keywordTokens), andsatisfiesis a name token. The generated position has no source-map entry, though, so this needs either esrap to write the keyword with a location (for example from the parser's tokens, like the delimiter mapping in feat: map delimiters and keywords inside nodes from the parser's tokens sveltejs/esrap#213) or an offset derived from the operand's generated end. Adopt esrap 2.3.11+ sourcemaps: pass tokens, drop boundaryTokens and the bracket guess once esrap #212/#213 land #235 prefers positions from the AST or the parser over arithmetic. - Map the whole expression. Add a verification-only mapping over the whole
satisfiesexpression, guarded on both ends having a source-map entry, as fix(core): map whole superclass expressions so base-class diagnostics report (#220) #222 does for superclass expressions. Narrower mappings sort first, so diagnostics inside the operand or type keep their own positions. With esrap 2.3.2, the end of a composite type ({ x: string },T[]) may have no entry (Map whole return types once esrap maps type-node boundaries #216), so the guard would skip those until the esrap upgrade.
Environment
origin/mainat 62ef14a,@tsrx/core0.3.3,@tsrx/typescript-plugin0.4.11,@tsrx/preact0.2.8- esrap 2.3.2 (lockfile); also checked with esrap 2.3.11 linked in its place
- TypeScript 5.9.3
- Node 24.18.0, pnpm 11.15.1, macOS 26.6.2
Additional Context
Found while fixing #223 (bare this), where this satisfies number was the one shape that still lost its diagnostic. Same family as #216 and #231: the walker has no mapping at the span TypeScript reports on.
Activity