You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We had this annotation everywhere in app-server APIs which made it so
that fields get serialized as `field?: T`, meaning if the field as
`None` we would omit the field in the payload. Removing this annotation
changes it so that we return `field: T | null` instead, which makes
codex app-server's API more aligned with the convention of public OpenAI
APIs like Responses.
Separately, remove the `#[ts(optional_fields = nullable)]` annotations
that were recently added which made all the TS types become `field?: T |
null` which is not great since clients need to handle undefined and
null.
I think generally it'll be best to have optional types be either:
- `field: T | null` (preferred, aligned with public OpenAI APIs)
- `field?: T` where we have to, such as types generated from the MCP
schema:
https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-06-18/schema.ts
(see changes to `mcp-types/`)
I updated @etraut-openai's unit test to check that all generated TS
types are one or the other, not both (so will error if we have a type
that has `field?: T | null`). I don't think there's currently a good use
case for that - but we can always revisit.
let offending_line_end = contents[line_start_idx..]
651
-
.find('\n')
652
-
.map(|i| line_start_idx + i)
653
-
.unwrap_or(contents.len());
654
-
let offending_snippet = contents[line_start_idx..offending_line_end].trim();
655
-
656
-
missing_optional_marker.insert(format!(
657
-
"{}:{}: {offending_snippet}",
658
-
path.display(),
659
-
line_number
660
-
));
661
-
662
716
search_start = abs_idx + 5;
663
717
}
664
718
}
@@ -670,12 +724,12 @@ mod tests {
670
724
"Generated TypeScript still includes unions with `undefined` in {undefined_offenders:?}"
671
725
);
672
726
673
-
// If this test fails, it means that a struct field that is `Option<T>` in Rust
674
-
// is being generated as `T | null` in TypeScript, without the optional marker
675
-
// (`?`). To fix this, add #[ts(optional_fields = nullable)] to the struct definition.
727
+
// If this assertion fails, it means a field was generated as
728
+
// "?: T | null" — i.e., both optional (undefined) and nullable (null).
729
+
// We only want either "?: T" or ": T | null".
676
730
assert!(
677
-
missing_optional_marker.is_empty(),
678
-
"Generated TypeScript has nullable fields without an optional marker: {missing_optional_marker:?}"
731
+
optional_nullable_offenders.is_empty(),
732
+
"Generated TypeScript has optional fields with nullable types (disallowed '?: T | null'), add #[ts(optional)] to fix:\n{optional_nullable_offenders:?}"
0 commit comments