typespec1-2 Exercise 2: Required, Nullable and Optional in Two Versions ============================================================================ reading.tsp: import "@typespec/http"; using Http; @service(#{ title: "Reading Service" }) namespace ReadingService; model Reading { value: float64; note: string | null; taken?: utcDateTime; } @route("/readings") interface Readings { @post add(@body reading: Reading): Reading; } Compiled twice with the openapi3 emitter, once with openapi-versions: 3.0.0 and once with 3.1.0, in separate output folders, then diffed: 1c1 < openapi: 3.0.0 --- > openapi: 3.1.0 36,37c36,38 < type: string < nullable: true --- > anyOf: > - type: string > - type: 'null' The 3.1.0 schema for Reading, from the compiler: Reading: type: object required: - value - note properties: value: type: number format: double note: anyOf: - type: string - type: 'null' taken: type: string format: date-time EXPLANATION value : required (in the required list) and not nullable: a number, format double (a 64-bit float). note : REQUIRED but NULLABLE: it is in the required list, so the key must be present, and its value may be a string or null. In 3.0.0 this is 'type: string' with 'nullable: true'; in 3.1.0 it is anyOf a string and 'type: null'. That is the only difference between the two files besides the version line. taken : OPTIONAL (the ? in the TypeSpec) and not nullable: not in the required list; a date-time string when present. WHY THIS WORKS AS AN ANSWER --------------------------- The three TypeSpec spellings (a plain type, | null, and ?) map to three different OpenAPI shapes, and only the nullable one changes between OpenAPI versions, because 3.1 dropped the nullable keyword when it adopted JSON Schema. This is why the choice of version matters when a generator reads the file: a tool that understands only 3.0's nullable may mishandle an anyOf with a null type.