Created
August 23, 2025 12:14
-
-
Save MaxMonteil/ddde26181623d63f9acd3527e16422cf to your computer and use it in GitHub Desktop.
TS typed JSON Patch RFC 6902
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Type definitions for JSON Patch, based on RFC 6902: | |
| * https://datatracker.ietf.org/doc/html/rfc6902 | |
| * | |
| * This file defines the TypeScript types for JSON Patch operations | |
| * (`add`, `remove`, `replace`, `move`, `copy`, `test`) and JSON Patch | |
| * documents. These types are intended to ensure type safety when | |
| * constructing or working with JSON Patch structures. | |
| * | |
| * @see RFC 6902 (JSON Patch): https://datatracker.ietf.org/doc/html/rfc6902 | |
| */ | |
| /** A string specifying a path in a JSON document. */ | |
| type JSONPointer = `/${string}` | |
| /** Allowed and serializable values for a JSON document. */ | |
| type JSONValue = | |
| | string | number | boolean | null | |
| | JSONValue[] | |
| | { [key: string]: JSONValue } | |
| /** The JSON Patch operations that MUST have a value property. */ | |
| type OpsWithValue = 'test' | 'add' | 'replace' | |
| /** The list of allowed Patch operations. */ | |
| type JSONPatchOp = OpsWithValue | 'remove' | 'move' | 'copy' | |
| /** | |
| * Generic accepted by JSONPatch operation types to specify: | |
| * - an optional `meta` property | |
| * - an optional `value` type for operations that require a value | |
| */ | |
| type JSONPatchGeneric<Op extends JSONPatchOp> = Op extends OpsWithValue | |
| ? { meta?: unknown, value?: JSONValue } | |
| : { meta?: unknown } | |
| /** Expands the properties required by {@link AbstractJSONPatchItem}, plus a `meta` type if given. */ | |
| type JSONPatchItem<T extends { meta?: unknown } = {}> = | |
| { path: JSONPointer } & | |
| (T extends { meta: infer M } ? { meta: M } : {}) | |
| /** | |
| * Extracts the `value` type from {@link JSONPatchGeneric} if given. | |
| * Defaults to `unknown` otherwise. | |
| */ | |
| type JSONPatchValue<T extends { value?: unknown } = {}> = T extends { value: infer V } ? V : unknown | |
| /** | |
| * A JSON Patch Add operation. | |
| * - inserts a value into an array at a given index | |
| * - adds a new value to an object if the member does not exist | |
| * - overwrites an object value if the member exists | |
| */ | |
| export type AddOp<T extends JSONPatchGeneric<'add'> = {}> = Beautify< | |
| JSONPatchItem<T> & | |
| { op: 'add'; value: JSONPatchValue<T> } | |
| > | |
| /** | |
| * A JSON Patch Remove operation. | |
| * Removes the value at the target location. | |
| * | |
| * Will fail if the target location does not exist. | |
| */ | |
| export type RemoveOp<T extends JSONPatchGeneric<'remove'>> = Beautify< | |
| JSONPatchItem<T> & | |
| { op: 'remove' } | |
| > | |
| /** | |
| * A JSON Patch Replace operation. | |
| * Replaces the value at a given location with a new value. | |
| * | |
| * Will fail if the target location does not exist. | |
| * | |
| * Equivalent to a {@link JSONPatchRemove} operation followed by a | |
| * {@link JSONPatchAdd} operation at the same location with the removed value. | |
| */ | |
| export type ReplaceOp<T extends JSONPatchGeneric<'replace'>> = Beautify< | |
| JSONPatchItem<T> & | |
| { op: 'replace'; value: JSONPatchValue<T> } | |
| > | |
| /** | |
| * A JSON Patch Move operation. | |
| * Removes the value at the given `from` location and adds it at the given | |
| * `path` location. | |
| * | |
| * Will fail if the `from` location does not exist. | |
| * | |
| * Equivalent to a {@link JSONPatchRemove} operation at the `from` location | |
| * followed by a {@link JSONPatchAdd} operation at the target location with | |
| * the removed value. | |
| */ | |
| export type MoveOp<T extends JSONPatchGeneric<'move'>> = Beautify< | |
| JSONPatchItem<T> & | |
| { op: 'move'; from: JSONPointer } | |
| > | |
| /** | |
| * A JSON Patch Copy operation. | |
| * Copies the value at the given `from` location and adds it at the given | |
| * `path` location. | |
| * | |
| * Will fail if the `from` location does not exist. | |
| * | |
| * Equivalent to a {@link JSONPatchAdd} operation at the target location | |
| * with the value at the given `from` location. | |
| */ | |
| export type CopyOp<T extends JSONPatchGeneric<'copy'>> = Beautify< | |
| JSONPatchItem<T> & | |
| { op: 'copy'; from: JSONPointer } | |
| > | |
| /** | |
| * A JSON Patch Test operation. | |
| * Tests that the value at the given location is equal to the given value. | |
| * | |
| * Will fail if the value at the given location is not equal | |
| * to the given value. | |
| * | |
| * - `strings`: are considered equal if they contain the same number of | |
| * Unicode characters and their code points are byte-by-byte equal. | |
| * | |
| * - `numbers`: are considered equal if their values are numerically equal. | |
| * | |
| * - `arrays`: are considered equal if they contain the same number of | |
| * values, and if each value can be considered equal to the value at | |
| * the corresponding position in the other array, using this list of | |
| * type-specific rules. | |
| * | |
| * - `objects`: are considered equal if they contain the same number of | |
| * members, and if each member can be considered equal to a member in | |
| * the other object, by comparing their keys (as strings) and their | |
| * values (using this list of type-specific rules). | |
| * | |
| * - `literals` (`false`, `true`, and `null`): are considered equal if they are | |
| * the same. | |
| */ | |
| export type TestOp<T extends JSONPatchGeneric<'test'> = {}> = Beautify< | |
| JSONPatchItem<T> & | |
| { op: 'test'; value: JSONPatchValue<T> } | |
| > | |
| /** | |
| * A JSON Patch document. | |
| * Represents an array of Path operations to apply to a target JSON document. | |
| * | |
| * An optional `Meta` type can be given to specify the type of an optional | |
| * `meta` property in each operation. | |
| * | |
| * This field is ignored when validating or applying the patch document. | |
| * | |
| * To use a stricter version that disallows `meta` see {@link StrictJSONPatch}. | |
| * | |
| * List of Patch operations: | |
| * - {@link AddOp} | |
| * - {@link RemoveOp} | |
| * - {@link ReplaceOp} | |
| * - {@link MoveOp} | |
| * - {@link CopyOp} | |
| * - {@link TestOp} | |
| */ | |
| export type JSONPatch<Meta = {}> = Array< | |
| | AddOp<{ meta: Meta }> | |
| | RemoveOp<{ meta: Meta }> | |
| | ReplaceOp<{ meta: Meta }> | |
| | MoveOp<{ meta: Meta }> | |
| | CopyOp<{ meta: Meta }> | |
| | TestOp<{ meta: Meta }> | |
| > | |
| /** | |
| * A JSON Patch document. | |
| * This more strictly matches RFC 6902 and prevents using and setting `meta`. | |
| * | |
| * To use a `meta` field, see {@link JSONPatch}. | |
| */ | |
| export type StrictJSONPatch = JSONPatch<never> | |
| /* UTILITY TYPES */ | |
| // The intersection here is to maintain optional properties as optional | |
| /** Cleans up the inferred type on hover. */ | |
| type Prettify<T> = { | |
| [K in keyof T as object extends Pick<T, K> ? K : never]?: T[K] | |
| } & { | |
| [K in keyof T as object extends Pick<T, K> ? never : K]: T[K] | |
| } | |
| /** Sometimes, being Pretty isn't enough. This is useful to normalize object intersections. */ | |
| type Beautify<T> = T extends object ? { [P in keyof T]: T[P] } : Prettify<T> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment