typespec1-3 Exercise 2: extends, is and Spread Compared ============================================================ vehicles.tsp: import "@typespec/http"; using Http; @service(#{ title: "Vehicle Service" }) namespace VehicleService; model Vehicle { id: string; wheels: int32; } model Insured { policy: string; insurer: string; } model Car extends Vehicle { doors: int32; } model Boat is Vehicle { draftMetres: float32; } model InsuredBoat { ...Boat; ...Insured; } @route("/v") interface V { @route("car") @get car(): Car; @route("boat") @get boat(): Boat; @route("insured") @get insured(): InsuredBoat; } The emitted schemas: components: schemas: Boat: type: object required: - id - wheels - draftMetres properties: id: type: string wheels: type: integer format: int32 draftMetres: type: number format: float Car: type: object required: - doors properties: doors: type: integer format: int32 allOf: - $ref: '#/components/schemas/Vehicle' Insured: type: object required: - policy - insurer properties: policy: type: string insurer: type: string InsuredBoat: type: object required: - id - wheels - draftMetres - policy - insurer properties: id: type: string wheels: type: integer format: int32 draftMetres: type: number format: float policy: type: string insurer: type: string Vehicle: type: object required: - id - wheels properties: id: type: string wheels: type: integer format: int32 WHAT EACH RELATIONSHIP BECAME Car extends Vehicle : only Car's own property (doors) plus an allOf reference to Vehicle. A client can treat a Car as a Vehicle. Boat is Vehicle : a full copy of Vehicle's properties (id, wheels) plus draftMetres, with no reference to Vehicle. (Vehicle has no base, so unlike the chapter's Puppy there is no allOf here.) InsuredBoat spreads Boat and Insured : every property of both, written out inline, with no references at all. (Insured appears as a schema of its own although nothing refers to it: every model in the namespace is emitted.) WHICH FOR 'A BOAT THAT HAS AN INSURANCE POLICY'? Spread the Insured fields (or add a nested insurance property of type Insured). A boat is not a kind of insurance, so extends is wrong; is would copy the wrong thing. Spreading gives a flat object, which is convenient for clients and means the policy fields cannot be separated from the boat's; a nested property (insurance: Insured) keeps them apart and makes insurance optional more naturally. Choose by whether clients think of the policy as part of the boat (flat) or something it has (nested). A note on Boat is Vehicle: it also shows why the example is a little unnatural. A boat does not have wheels. It would be better to define a base without wheels, and spread the parts that belong. The contract is allowed to be wrong; the point is to see that the compiler will not tell you. WHY THIS WORKS AS AN ANSWER --------------------------- The three relationships look similar in TypeSpec and produce visibly different contracts. Only extends keeps a link a client can use; is and spread both flatten. Choosing among them is a design decision about the API's users, not about how much typing you save.