v3.18.0: Five Fixes Live. Now Help Us Find the Sixth π¦
54
Publication Date: 12/03/2026
Author: Anas Chakroun
6-7 minutes
The AST engine reads your code, climbs the syntax tree, identifies what you've selected, and decides exactly where to place the log. In theory, it handles everything. In practice, real-world code is never as predictable as test cases.
v3.18.0 is a hunting release. No new features. Just five real patterns pulled from real codebases that exposed subtle but impactful bugs in the engine, with the story behind each one.
Why the Engine Gets It Wrong
Turbo's AST engine classifies every selected variable into one of ~15 log message types, each with its own line insertion logic. Get the type wrong and the log lands at the wrong line: sometimes between array elements, sometimes before the variable exists, sometimes inside a return statement, breaking your code.
The tricky part: each bug only surfaces on a specific combination of syntax. A class field initializer with a new expression. A TypeScript constructor parameter wrapped in private. An identifier buried four nodes deep inside a multi-line array spread. These aren't contrived edge cases; they're patterns that appear constantly in Angular, React, and TypeScript codebases.
We Found Five. You\'ll Find More.
These bugs came to light because someone ran Turbo against real code and noticed something was off. Most edge cases don't get reported. They just quietly erode trust in the tool.
We built a dedicated page for this. If Turbo places a log in the wrong spot, paste the snippet, describe what happened, and tell us what you expected. Every submission becomes a failing test case that drives a fix.
π¬ Report an Edge Case
Found a pattern that Turbo handles incorrectly? Tell us about it on the Edge Cases Reporting page. Every report goes directly into our bug queue.

Five Patterns, Five Fixes
1 β Ternary Destructuring
Selecting cookieName here produced a wrong log type and inserted the log one line too early, before the variable was assigned. The ternary checker and line calculator only handled a plain identifier on the left-hand side. Array and object destructuring patterns were never considered. Fix: both helpers now recognize ArrayPattern and ObjectPattern, identify the ternary initializer, and insert the log after the full block.
2 β TypeScript Constructor Access Modifiers
Selecting backend inside this Angular-style constructor failed to classify it as a function parameter. The log was inserted at entirely the wrong line. TypeScript access modifiers (private, public, protected, readonly) wrap the parameter in a TSParameterProperty node that the checker never unwrapped. Fix: the parameter checker now unwraps TSParameterProperty and inspects the inner parameter, correctly classifying access-modified constructor parameters.
3 β Identifier Buried Deep in a Multi-Line Expression
Selecting HTTP_INTERCEPTOR_FNS caused the log to be inserted between the array elements, producing invalid code. The parent-climbing algorithm stopped at the identifier when the immediate parent had a different start offset, yielding a position deep inside the expression rather than adjacent to the statement. Fix: the climb now continues until it reaches an enclosing statement boundary. Multi-line statements get the log inserted before; single-line statements get it after.
4 β Method Call Inside a Return Statement
Selecting rawMessagePart here triggered the PropertyMethodCall classifier (priority 11) instead of WithinReturnStatement (priority 15). Because PropertyMethodCall has a higher rank in the type table, the return handler never got a chance to match, and the log landed at the wrong line. Fix: the PropertyMethodCall checker now builds an ancestor map and rejects matches where the call is nested inside a ReturnStatement, letting the correct type claim it.
5 β Class Property Initialized with new
Selecting config failed to classify it as a FunctionCallAssignment. Two gaps combined: the function call detector only recognized CallExpression nodes (foo()), never NewExpression (new Foo()); and the walker only walked VariableDeclaration nodes, never PropertyDefinition (class fields). Fix: NewExpression is now recognized alongside CallExpression, and class field initializers are fully walked and classified.
One Pattern. One Bad Log. One Broken Debugging Session.
Each of these bugs would silently produce a log in the wrong place. In the best case, you'd notice immediately and manually move it. In the worst case, you'd ship invalid code or waste minutes tracing why your debugging output doesn't match what you selected.
What makes them hard to pre-empt: none of these patterns are contrived. They all come from production codebases: Angular HTTP interceptors, in-memory backend services, cookie parsing utilities. If you write TypeScript at any scale, you've likely hit one of these.
v3.18.0 is already running in your editor. All five fixes are live. The Angular HTTP interceptor, the TypeScript constructor parameters, the deeply nested identifiers, the return statement method calls, the class field initializers. All handled correctly now. Try them.
And if you find the sixth one, we want to hear about it.