diff --git a/backend/src/utils.ts b/backend/src/utils.ts index 059ddac9..26889b3d 100644 --- a/backend/src/utils.ts +++ b/backend/src/utils.ts @@ -95,6 +95,24 @@ function typeIsArray(type: any): type is t.ArrayC { return type._tag === "ArrayType"; } +export const tNormalizedNullOrUndefined = new t.Type( + "tNormalizedNullOrUndefined", + (v): v is undefined => typeof v === "undefined", + (v, c) => (v == null ? t.success(undefined) : t.failure(v, c, "Value must be null or undefined")), + s => undefined, +); + +/** + * Similar to `tNullable`, but normalizes both `null` and `undefined` to `undefined`. + * This allows adding optional config options that can be "removed" by setting the value to `null`. + */ +export function tNormalizedNullOptional>(type: T) { + return t.union( + [type, tNormalizedNullOrUndefined], + `Optional<${type.name}>`, // Simplified name for errors and config schema views + ); +} + export type TDeepPartial = T extends t.InterfaceType ? TDeepPartialProps : T extends t.DictionaryType