Skip to content

Commit

Permalink
Add Jsonify support for optional keys sindresorhus#424
Browse files Browse the repository at this point in the history
  • Loading branch information
skarab42 committed Aug 26, 2022
1 parent e6d412f commit 3632d5d
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 8 deletions.
34 changes: 28 additions & 6 deletions source/jsonify.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
import type {JsonPrimitive, JsonValue} from './basic';
import type {Merge} from './merge';
import type {NegativeInfinity, PositiveInfinity} from './numeric';
import type {TypedArray} from './typed-array';

// Note: The return value has to be `any` and not `unknown` so it can match `void`.
type NotJsonable = ((...args: any[]) => any) | undefined | symbol;

// Returns never if the key or property is not jsonable without testing whether the property is required or optional otherwise return the key.
type BaseKeyFilter<Type, Key extends keyof Type> = Key extends symbol
? never
: Type[Key] extends symbol
? never
: [(...args: any[]) => any] extends [Type[Key]]
? never
: Key;

// Returns never if the key or property is not jsonable or optional otherwise return the key.
type RequiredKeyFilter<Type, Key extends keyof Type> = undefined extends Type[Key]
? never
: BaseKeyFilter<Type, Key>;

// Returns never if the key or property is not jsonable or required otherwise return the key.
type OptionalKeyFilter<Type, Key extends keyof Type> = undefined extends Type[Key]
? Type[Key] extends undefined
? never
: BaseKeyFilter<Type, Key>
: never;

/**
Transform a type to one that is assignable to the `JsonValue` type.
Expand Down Expand Up @@ -82,9 +104,9 @@ export type Jsonify<T> =
: T extends TypedArray ? Record<string, number>
: T extends any[]
? {[I in keyof T]: T[I] extends NotJsonable ? null : Jsonify<T[I]>}
: {[P in keyof T as P extends symbol ? never
: T[P] extends NotJsonable ? never
: P
]: Jsonify<Required<T>[P]>} // Recursive call for its children
: never // Otherwise any other non-object is removed
: never; // Otherwise non-JSONable type union was found not empty
: Merge<
{[Key in keyof T as RequiredKeyFilter<T, Key>]: Jsonify<T[Key]>},
{[Key in keyof T as OptionalKeyFilter<T, Key>]?: Jsonify<Exclude<T[Key], undefined>>}
> // Recursive call for its children
: never // Otherwise any other non-object is removed
: never; // Otherwise non-JSONable type union was found not empty
40 changes: 38 additions & 2 deletions test-d/jsonify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,5 +235,41 @@ declare const jsonifiedOptionalTypeUnion: Jsonify<OptionalTypeUnion>;
declare const jsonifiedNonOptionalTypeUnion: Jsonify<NonOptionalTypeUnion>;

expectType<{a?: string}>(jsonifiedOptionalPrimitive);
expectType<{a?: never}>(jsonifiedOptionalTypeUnion);
expectType<{a: never}>(jsonifiedNonOptionalTypeUnion);
expectType<{}>(jsonifiedOptionalTypeUnion);
expectType<{a?: string}>(jsonifiedNonOptionalTypeUnion);

// Test for 'Jsonify support for optional object keys, unserializable object values' #424
// See https://github.com/sindresorhus/type-fest/issues/424
type AppData = {
// Should be kept
requiredString: string;
requiredUnion: number | boolean;

// Should be kept and set to optional
optionalString?: string;
optionalUnion?: number | string;
optionalStringUndefined: string | undefined;
optionalUnionUndefined: number | string | undefined;

// Should be omitted
requiredFunction: () => any;
optionalFunction?: () => any;
requiredFunctionUnion: string | (() => any);
optionalFunctionUnion?: string | (() => any);
optionalFunctionUndefined: (() => any) | undefined;
optionalFunctionUnionUndefined: string | (() => any) | undefined;
};

type ExpectedAppDataJson = {
requiredString: string;
requiredUnion: number | boolean;

optionalString?: string;
optionalUnion?: string | number;
optionalStringUndefined?: string;
optionalUnionUndefined?: string | number;
};

declare const response: Jsonify<AppData>;

expectType<ExpectedAppDataJson>(response);

0 comments on commit 3632d5d

Please sign in to comment.