Romaji to Kana Converter: Angular & Express — Chapter 4, Exercise 3 ===================================================================== TASK In your own words, explain why convertText() returning void and updating signals internally is what lets Chapter 5 swap its own implementation for a real HTTP call with zero changes to either component. Then describe specifically what WOULD have had to change in both RomajiInputComponent and KanaOutputComponent if convertText() had instead directly returned a { hiragana, katakana } object for the caller to handle itself. ANSWER Why the void-returning, signal-updating design costs nothing later: RomajiInputComponent and KanaOutputComponent were never written against "how convertText() gets its answer" -- they were written against two completely separate, narrower contracts: - RomajiInputComponent only ever CALLS convertText(value). It never looks at what convertText() returns, because it doesn't return anything (void). Its job ends the moment the call is made. - KanaOutputComponent only ever READS this.convertService.hiragana and .katakana -- two signals it was handed once, at construction time, that it re-reads on every change detection cycle. It has no idea those values came from convertText() at all, let alone HOW convertText() produced them. Because neither component's own code path runs through convertText()'s internal logic, replacing "call convert() directly and set two signals" with "call this.http.post(...) and set the same two signals from inside .subscribe()" only touches code that already lived entirely inside ConvertService. The method's name stays convertText, its parameter stays a single string, and it still returns void either way -- from the outside, the method's own shape never moves, only what happens between the opening and closing brace does. What WOULD have had to change if convertText() returned the result directly instead: If convertText() were instead written as: convertText(romaji: string): { hiragana: string; katakana: string } { return convert(romaji); } ...then RomajiInputComponent's onInput() would have had to actually DO something with that return value -- most likely pass it along to KanaOutputComponent itself (via an @Input(), or a shared signal it now has to set manually), since RomajiInputComponent is the one holding the result the moment convertText() returns it. The real problem shows up the moment Chapter 5 arrives: an HTTP call through HttpClient doesn't return a value synchronously -- it returns an Observable, which only produces its value later, inside a .subscribe() callback. A method typed to return { hiragana: string; katakana: string } directly simply CANNOT be rewritten to make a real network call internally, because there is no way to synchronously return a value that hasn't arrived yet. Fixing that would force convertText()'s own return type to change -- to an Observable, or a Promise -- which would in turn force RomajiInputComponent to change how it calls the method (adding a .subscribe() or an await it didn't need before), which is exactly the kind of component-level change the actual Chapter 4 design was built specifically to avoid. WHY THIS WORKS AS AN ANSWER ---------------------------- It correctly identifies that the real payoff is a strict separation between "how a caller triggers a conversion" (a plain method call, unaffected by internals) and "how a caller reads a conversion's own result" (reading a signal, also unaffected by internals) -- and it correctly traces the concrete failure mode of the return-a-value alternative: a synchronous return type is fundamentally incompatible with the async nature of the real HTTP call Chapter 5 introduces, forcing a cascading signature change the signal-based design avoids entirely.