The error you already know
You have a reply that is either text or a tool call, and you sort the two the way you always have, an isinstance ladder that walks down the variants. Ported straight across, the reflex reaches for instanceof, and the compiler stops you on the first rung.
type TextReply = { text: string };type ToolCall = {name: string;args: string;};// the isinstance ladder, ported// straight acrossfunction render(msg: TextReply | ToolCall,): string {if (msg instanceof TextReply'TextReply' only refers to a type, but is being used as a value here.)return msg.text;if (msg instanceof ToolCall)return msg.name;return "";}
TextReply is only a type. It describes a shape and then vanishes before the program runs, so there is no class in memory for instanceof to test against. The union already holds both shapes. All you need is a question you can ask about the value in front of you.
reply.ts(7,22) 'TextReply' only refers to a type, but is being used as a value here.
Watch the habit become the idiom
The fix is a different question. Ask what the value can answer for itself, and the class hierarchy flattens into a union the compiler can read.
Step through it at your own pace. Each click is one move.
Step 1 of 4
Good Python. Two dataclasses and an isinstance ladder, exactly what you were taught.
@dataclass
class TextReply:
text: str
@dataclass
class ToolCall:
name: str
def render(msg):
if isinstance(msg, TextReply):
return msg.text
return msg.nameHere's what each move does.
- Start with the Python habit, two classes and an isinstance ladder that names each variant.
- Port it and the classes earn nothing. They exist only to give
instanceofsomething to test. - Drop the classes. A reply becomes a union of object types, narrowed by asking a question about the value.
- Add a third reply and it costs one more member and one more check. The union stays flat.
That last move leans on the variants keeping different fields, since asking whether text is present can only sort them while one has text and the other does not. When two variants start sharing their fields, presence stops being enough, and you give each one a small field whose only job is to name it. That field is where lesson 06 picks up.
Now make the compiler agree
Reply is a union of two variants with no base type above them. Finish render so it returns the text for a TextReply and the tool name for a ToolCall. Narrow with a check the compiler understands, the in operator reads well here, and skip the classes. When each branch reads a field that exists, the compiler runs out of complaints.