Last active
August 9, 2023 12:48
-
-
Save ehvattum/7d68a63be96911580ad7d06dfb946abb to your computer and use it in GitHub Desktop.
Create a JSONPatch-ish type for any object
This file contains 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
import { Primitive, Simplify, WritableKeysOf } from "type-fest" | |
/** | |
* Create a JSON-patch dto type for a given type. | |
* * Strips out readonly properties (WritableKeysOf) | |
*/ | |
export type Patch<T> = PatchRoot<Pick<T, WritableKeysOf<T>>> | |
/** Make patch schema for all useful keys in type */ | |
type PatchRoot<TRoot extends object> = { | |
[Key in keyof TRoot]: Key extends string ? Simplify<Operations<Key, TRoot[Key]>> : never | |
}[keyof TRoot][] | |
/** Define operations for a property */ | |
type Operations<TKey extends string, TValue> = [TValue] extends [(infer Item)[]] | |
? ArrayOperations<TKey, Item> | |
: TValue extends Simple | |
? SimpleOperation<TKey, TValue> | |
: TValue extends object | |
? ObjectOperations<TKey, TValue> | |
: never | |
/** Define operations for nested properties */ | |
type ObjectOperations<OwnName extends string, TObject extends object> = { | |
[Key in keyof TObject]: Key extends string | |
? Operations<`${OwnName}.${Key}`, TObject[Key]> | |
: never | |
}[keyof TObject] | |
/** Define operations for properties on objects in an Array */ | |
type Recurse<Key extends string, ItemType> = Operations<`${Key}[${number}]`, ItemType> | |
/** Define operations for array properties and for operations on items in the array */ | |
type ArrayOperations<TKey extends string, TItem> = | |
| RemoveOp<TKey> | |
| ReplaceOp<TKey, TItem[]> | |
| PushOp<TKey, TItem> | |
| InjectAtOp<TKey, TItem> | |
| SetAtOp<TKey, TItem> | |
| RemoveAtOp<TKey> | |
| Recurse<TKey, TItem> // Add in object-operations for items in the array | |
/** Basic value types */ | |
type Simple = Primitive | |
/** | |
* Creates a type that describes the patch operation for a simple type | |
*/ | |
type SimpleOperation<Key extends string, Value extends Simple> = | |
| ReplaceOp<Key, Value> | |
| (Value extends undefined ? RemoveOp<Key> : never) | |
/** Removes the entire property */ | |
export type RemoveOp<Key extends string> = { | |
path: Key | |
op: "remove" | |
} | |
/** Replaces the entire property */ | |
export type ReplaceOp<Key extends string, Value> = Value extends undefined | |
? never | |
: { | |
path: Key | |
op: "replace" | |
value: Value | |
} | |
/** Adds an item to the end of the array */ | |
export type PushOp<Key extends string, ItemType> = { | |
path: Key | |
op: "add" | |
value: ItemType | |
} | |
/** Injects item at index, moves existing items out of the way. */ | |
export type InjectAtOp<Key extends string, ItemType> = { | |
path: `${Key}[${number}]` | |
op: "add" | |
value: ItemType | |
} | |
/** Replaces the item at index. */ | |
export type SetAtOp<Key extends string, ItemType> = { | |
path: `${Key}[${number}]` | |
op: "replace" | |
value: ItemType | |
} | |
/** Removes the item at index. */ | |
export type RemoveAtOp<Key extends string> = { | |
path: `.${Key}[${number}]` | |
op: "remove" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment