TS compilation issue
TypeScript Compiler Crash with moduleResolution: "bundler"
Summary
The live_toast dependency causes a TypeScript compiler crash when imported in projects using moduleResolution: "bundler". The crash occurs with an internal TypeScript error: "Debug Failure. False expression" in the getConstructorDefinedThisAssignmentTypes function.
Environment
- TypeScript Version: 5.4.5 (also tested with 5.8.3)
- Node.js Version: v22.12.0
- Operating System: macOS 24.5.0
- live_toast Version: [version from deps/live_toast]
TypeScript Configuration
{
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "ES2020"],
"allowJs": true,
"skipLibCheck": true,
"types": ["vite/client"],
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "bundler",
"isolatedModules": true,
"resolveJsonModule": true,
"noEmit": true,
"jsx": "react"
}
}
Reproduction Steps
- Create a TypeScript project with
moduleResolution: "bundler" - Import
createLiveToastHookfrom live_toast:import { createLiveToastHook } from "../../deps/live_toast"; - Run TypeScript compilation:
npx tsc
Expected Behavior
TypeScript compilation should complete successfully.
Actual Behavior
TypeScript compiler crashes with the following error:
Error: Debug Failure. False expression.
at getConstructorDefinedThisAssignmentTypes (/path/to/node_modules/typescript/lib/tsc.js:52803:11)
at getWidenedTypeForAssignmentDeclaration (/path/to/node_modules/typescript/lib/tsc.js:52614:78)
at getTypeOfVariableOrParameterOrPropertyWorker (/path/to/node_modules/typescript/lib/tsc.js:53029:14)
at getTypeOfVariableOrParameterOrProperty (/path/to/node_modules/typescript/lib/tsc.js:52974:20)
at getTypeOfSymbol (/path/to/node_modules/typescript/lib/tsc.js:53306:14)
at getWriteTypeOfSymbol (/path/to/node_modules/typescript/lib/tsc.js:53284:29)
at checkPropertyAccessExpressionOrQualifiedName (/path/to/node_modules/typescript/lib/tsc.js:70818:116)
at checkPropertyAccessExpression (/path/to/node_modules/typescript/lib/tsc.js:70576:94)
at checkExpressionWorker (/path/to/node_modules/typescript/lib/tsc.js:76295:16)
at checkExpression (/path/to/node_modules/typescript/lib/tsc.js:76216:32)
Root Cause Analysis
The issue appears to be caused by the global interface extension in live_toast.ts:
declare global {
interface HTMLElement {
order: number
targetDestination: string
}
}
When TypeScript uses moduleResolution: "bundler", it encounters a bug in its type inference system when processing property access chains on these extended global interfaces, particularly in the context of constructor-defined property assignments.
Workarounds Attempted
- ✅ Skipping TypeScript compilation - Works but loses type checking benefits
- ❌ Using
@ts-ignore- Doesn't prevent the crash - ❌ Excluding files with
exclude- Still processes imports - ❌ Creating wrapper files - Still triggers the crash
- ❌ Custom type declarations - Doesn't resolve the internal compiler bug
Working Workaround
Currently, the only viable workaround is to skip TypeScript type checking in the build process:
{
"scripts": {
"build": "vite build" // instead of "tsc && vite build"
}
}
Suggested Fixes
-
Modify the global interface extension to use optional properties:
declare global { interface HTMLElement { order?: number; targetDestination?: string; } } -
Use a more specific interface instead of extending the global HTMLElement:
interface LiveToastElement extends HTMLElement { order: number; targetDestination: string; } -
Add a TypeScript configuration note in the documentation about moduleResolution compatibility
Additional Context
- The crash occurs specifically with
moduleResolution: "bundler" - Works fine with
moduleResolution: "node" - The issue is reproducible across different TypeScript versions (5.4.5 and 5.8.3)
- This affects Phoenix LiveView projects using modern TypeScript configurations
Impact
This issue prevents developers from using modern TypeScript configurations (moduleResolution: "bundler") with live_toast, forcing them to either:
- Use older module resolution strategies
- Skip type checking entirely
- Avoid using live_toast
Would appreciate any guidance on resolving this TypeScript compatibility issue!