@@ -81,16 +81,27 @@ auto TypeScript::operator()(const IREnumeration &entry) const -> void {
8181
8282auto TypeScript::operator ()(const IRObject &entry) const -> void {
8383 const auto type_name{sourcemeta::core::mangle (entry.pointer , this ->prefix )};
84- const auto has_additional{entry.additional .has_value ()};
84+ const auto has_typed_additional{
85+ std::holds_alternative<IRType>(entry.additional )};
86+ const auto allows_any_additional{
87+ std::holds_alternative<bool >(entry.additional ) &&
88+ std::get<bool >(entry.additional )};
8589
86- if (has_additional && entry.members .empty ()) {
90+ if (has_typed_additional && entry.members .empty ()) {
8791 this ->output << " export type " << type_name << " = Record<string, "
88- << sourcemeta::core::mangle (entry.additional ->pointer ,
89- this ->prefix )
92+ << sourcemeta::core::mangle (
93+ std::get<IRType>(entry.additional ).pointer ,
94+ this ->prefix )
9095 << " >;\n " ;
9196 return ;
9297 }
9398
99+ if (allows_any_additional && entry.members .empty ()) {
100+ this ->output << " export type " << type_name
101+ << " = Record<string, unknown>;\n " ;
102+ return ;
103+ }
104+
94105 this ->output << " export interface " << type_name << " {\n " ;
95106
96107 // We always quote property names for safety. JSON Schema allows any string
@@ -110,20 +121,29 @@ auto TypeScript::operator()(const IRObject &entry) const -> void {
110121 << " ;\n " ;
111122 }
112123
113- if (has_additional) {
124+ if (allows_any_additional) {
125+ this ->output << " [key: string]: unknown | undefined;\n " ;
126+ } else if (has_typed_additional) {
114127 // TypeScript index signatures must be a supertype of all property value
115128 // types. We use a union of all member types plus the additional properties
116129 // type plus undefined (for optional properties).
117130 this ->output << " [key: string]:\n " ;
131+ this ->output << " // As a notable limitation, TypeScript requires index "
132+ " signatures\n " ;
133+ this ->output << " // to also include the types of all of its "
134+ " properties, so we must\n " ;
135+ this ->output << " // match a superset of what JSON Schema allows\n " ;
118136 for (const auto &[member_name, member_value] : entry.members ) {
119137 this ->output << " "
120138 << sourcemeta::core::mangle (member_value.pointer ,
121139 this ->prefix )
122140 << " |\n " ;
123141 }
142+
124143 this ->output << " "
125- << sourcemeta::core::mangle (entry.additional ->pointer ,
126- this ->prefix )
144+ << sourcemeta::core::mangle (
145+ std::get<IRType>(entry.additional ).pointer ,
146+ this ->prefix )
127147 << " |\n " ;
128148 this ->output << " undefined;\n " ;
129149 }
0 commit comments