@@ -45,6 +45,12 @@ export function escapeObjectKey(key: string): string {
4545
4646/**
4747 * Prevents narrowing of string literal union to string.
48+ *
49+ * @example
50+ * ```ts
51+ * type ThisWillBeNarrowedToString = string | "string" | "number" | "boolean";
52+ * type ThisWillNotBeNarrowed = NonNullable<string> | "string" | "number" | "boolean";
53+ * ```
4854 */
4955function toSafeUnionString (
5056 type : string | undefined ,
@@ -53,6 +59,7 @@ function toSafeUnionString(
5359 if ( type === "string" && types . length > 1 ) {
5460 return "NonNullable<string>" ;
5561 }
62+
5663 return type ;
5764}
5865
@@ -138,9 +145,26 @@ export function toSchemaType(
138145 console . groupEnd ( ) ;
139146 }
140147
148+ // The string union is safe if all types are boolean, string, number, integer,
149+ // object, array or null. Any other value is not known to be safe and we therefore
150+ // use the `toSafeUnionString` function to prevent narrowing of the type.
151+ const safeStringUnion = schema . anyOf . every ( ( schema ) =>
152+ "type" in schema &&
153+ ! ( "enum" in schema || "oneOf" in schema ||
154+ "anyOf" in schema || "allOf" in schema ) &&
155+ ( schema . type === "boolean" || schema . type === "string" ||
156+ schema . type === "number" || schema . type === "integer" ||
157+ schema . type === "object" || schema . type === "array" ||
158+ schema . type === "null" )
159+ ) ;
160+
141161 return schema . anyOf
142162 . map ( ( schema ) => toSchemaType ( document , schema , coerceToString ) )
143- . map ( ( type , _ , types ) => toSafeUnionString ( type , types ) )
163+ . map ( ( type , _ , types ) =>
164+ safeStringUnion ? type : toSafeUnionString ( type , types )
165+ )
166+ // Naively removes duplicates.
167+ . filter ( ( type , index , types ) => types . indexOf ( type ) === index )
144168 . filter ( Boolean )
145169 . join ( "|" ) ;
146170 }
0 commit comments