fvtt-types
Version:
TypeScript type definitions for Foundry VTT
823 lines (724 loc) • 30.4 kB
text/typescript
import type {
AnyArray,
AnyConstructor,
AnyFunction,
AnyObject,
DeepReadonly,
DeletableDotKeys,
InexactPartial,
NonNullish,
} from "#utils";
import type Document from "../abstract/document.d.mts";
/**
* Benchmark the performance of a function, calling it a requested number of iterations.
* @param func - The function to benchmark
* @param iterations - The number of iterations to test
* @param args - Additional arguments passed to the benchmarked function
*/
export function benchmark<F extends AnyFunction>(func: F, iterations: number, ...args: Parameters<F>): Promise<void>;
/**
* A debugging function to test latency or timeouts by forcibly locking the thread for an amount of time.
* @param ms - A number of milliseconds to lock
* @param debug - Log debugging information? (default: `false`)
*/
export function threadLock(ms: number, debug?: boolean): Promise<void>;
/**
* Wrap a callback in a debounced timeout.
* Delay execution of the callback function until the function has not been called for delay milliseconds
* @param callback - A function to execute once the debounced threshold has been passed
* @param delay - An amount of time in milliseconds to delay
* @returns A wrapped function which can be called to debounce execution
*/
export function debounce<T extends AnyFunction>(callback: T, delay: number): (...args: Parameters<T>) => void;
/**
* Wrap a callback in a throttled timeout.
* Delay execution of the callback function when the last time the function was called was delay milliseconds ago
* @param callback - A function to execute once the throttled threshold has been passed
* @param delay - A maximum amount of time in milliseconds between to execution
* @returns A wrapped function which can be called to throttle execution
*/
export function throttle<T extends (...args: any[]) => any>(callback: T, delay: number): T;
/**
* A utility function to reload the page with a debounce.
*/
export const debouncedReload: () => void;
/**
* Recursively freezes ({@linkcode Object.freeze}) the object (or value).
* This method DOES NOT support cyclical data structures.
* This method DOES NOT support advanced object types like Set, Map, or other specialized classes.
* @param obj - The object (or value)
* @param options - Options to configure the behaviour of deepFreeze
* @returns The same object (or value) that was passed in
* @remarks
* @throws If passed an object with a depth over 100, or if it encounters anything but a plain object, array, or primitive
*/
export function deepFreeze<const T extends AnyObject>(obj: T, options?: DeepFreezeOptions): DeepReadonly<T>;
/** @internal */
type _DeepFreezeOptions = InexactPartial<{
/**
* Throw an Error if deepFreeze is unable to freeze something instead of returning the original
* @defaultValue `false`
*/
strict: boolean;
}>;
export interface DeepFreezeOptions extends _DeepFreezeOptions {}
/**
* Recursively seals ({@linkcode Object.seal}) the object (or value).
* This method DOES NOT support cyclical data structures.
* This method DOES NOT support advanced object types like Set, Map, or other specialized classes.
* @param obj - The object (or value)
* @param options - Options to configure the behaviour of deepSeal
* @returns The same object (or value) that was passed in
* @remarks
* @throws If passed an object with a depth over 100, or if it encounters anything but a plain object, array, or primitive
*/
export function deepSeal<T extends AnyObject>(obj: T, options?: DeepSealOptions): T;
/** @internal */
type _DeepSealOptions = InexactPartial<{
/**
* Throw an Error if deepSeal is unable to seal something
* @defaultValue `false`
*/
strict: boolean;
}>;
export interface DeepSealOptions extends _DeepSealOptions {}
/**
* Quickly clone a simple piece of data, returning a copy which can be mutated safely.
* This method DOES support recursive data structures containing inner objects or arrays.
* This method DOES NOT support cyclical data structures.
* This method DOES NOT support advanced object types like Set, Map, or other specialized classes.
* @param original - Some sort of data
* @param options - Options to configure the behaviour of deepClone
* @returns The clone of that data
* @remarks
* @throws If passed an object with a depth over 100, or if it encounters anything but a plain object, array, `Date`, or primitive
*/
export function deepClone<T>(original: T, options?: DeepCloneOptions): T;
/** @internal */
type _DeepCloneOptions = InexactPartial<{
/**
* Throw an Error if deepClone is unable to clone something instead of returning the original
* @defaultValue `false`
*/
strict: boolean;
}>;
export interface DeepCloneOptions extends _DeepCloneOptions {}
/**
* Deeply difference an object against some other, returning the update keys and values.
* @param original - An object comparing data against which to compare
* @param other - An object containing potentially different data
* @param options - Additional options which configure the diff operation
* @returns An object of the data in other which differs from that in original
*/
export function diffObject(original: object, other: object, options?: DiffObjectOptions): object;
/** @internal */
type _DiffObjectOptions = InexactPartial<{
/**
* Only recognize differences in other for keys which also exist in original
* @defaultValue `false`
*/
inner: boolean;
/**
* Apply special logic to deletion keys. They will only be kept if the original object has a
* corresponding key that could be deleted.
* @defaultValue `false`
*/
deletionKeys: boolean;
/**
* An internal depth tracker
* @defaultValue `0`
* @remarks Not intended to be passed externally. v13 converted the outer signature of `deepClone` to hide this param,
* and that pattern was copied for `deepFreeze` and `deepSeal`; Not sure why they didn't use it here.
*/
_d: number;
}>;
export interface DiffObjectOptions extends _DiffObjectOptions {}
/**
* Recurse through an object, applying all special keys.
* Deletion keys ("-=") are removed.
* Forced replacement keys ("==") are assigned.
* @remarks Returns the passed `obj` for anything but plain objects and arrays.
*/
// TODO: bespoke return type accounting for deletion keys recursively
export function applySpecialKeys<T>(obj: T): T;
/**
* Test if two objects contain the same enumerable keys and values.
* @param a - The first object.
* @param b - The second object.
*/
export function objectsEqual<B extends object>(a: object, b: B): a is B;
/**
* A cheap data duplication trick which is relatively robust.
* For a subset of cases the deepClone function will offer better performance.
* @param original - Some sort of data
* @template T - Type of the original data.
*
* @remarks This function will actually convert any occurrences of `NaN` and `Infinity` to `null`. For ease of use, this
* is _not_ reflected in the type. Be careful if your types might contain `NaN` or `Infinity`!
*/
export function duplicate<T>(original: T): Duplicated<T>;
/**
* The resulting type when using {@linkcode duplicate} on some data of type `T`.
*
* @template T - Original type.
* @internal
*/
export type Duplicated<T> = T extends NonStringifiable ? never : InnerDuplicated<T>;
/**
* Is a string key of an object used for certain deletion or forced replacement operations.
* @remarks Foundry seems to refer to both `-=...` and `==...` as "deletion keys" in this context
*/
export function isDeletionKey(key: string): key is DeletionKey;
export type DeletionKey = `-=${string}` | `==${string}`;
/**
* Test whether some class is a subclass of a parent.
* Returns true if the classes are identical.
* @param cls - The class to test
* @param parent - Some other class which may be a parent
* @returns Is the class a subclass of the parent?
*/
export function isSubclass<Parent extends AnyConstructor>(cls: AnyConstructor, parent: Parent): cls is Parent;
/**
* Search up the prototype chain and return the class that defines the given property.
* @param obj - A class instance or class definition which contains a property.
*
* If a class instance is passed the property is treated as an instance attribute.
*
* If a class constructor is passed the property is treated as a static attribute.
* @param property - The property name
* @returns The class that defines the property
*/
export function getDefiningClass(obj: AnyConstructor, property: string): AnyConstructor;
/**
* Encode an url-like string by replacing any characters which need encoding
* @param path - A fully-qualified URL or url component (like a relative path)
* @returns An encoded URL string
*/
export function encodeURL(path: string): string;
/**
* Expand a flattened object to be a standard multi-dimensional nested Object by converting all dot-notation keys to
* inner objects.
*
* @param obj - The object to expand
* @returns An expanded object
*
*/
export function expandObject(obj: object): object;
/**
* Filter the contents of some source object using the structure of a template object.
* Only keys which exist in the template are preserved in the source object.
*
* @param source - An object which contains the data you wish to filter
* @param template - An object which contains the structure you wish to preserve
* @param options - Additional options which customize the filtration (default: `{}`)
* @returns The filtered object
*
* @example Filter an object
* ```typescript
* const source = {foo: {number: 1, name: "Tim", topping: "olives"}, bar: "baz"};
* const template = {foo: {number: 0, name: "Mit", style: "bold"}, other: 72};
* filterObject(source, template); // {foo: {number: 1, name: "Tim"}};
* filterObject(source, template, {templateValues: true}); // {foo: {number: 0, name: "Mit"}};
* ```
*/
export function filterObject(source: object, template: object, options?: FilterObjectOptions): object;
/** @internal */
type _FilterObjectOptions = InexactPartial<{
/**
* Whether to keep deletion keys
* @defaultValue `false`
*/
deletionKeys: boolean;
/**
* Instead of keeping values from the source, instead draw values from the template
* @defaultValue `false`
*/
templateValues: boolean;
}>;
export interface FilterObjectOptions extends _FilterObjectOptions {}
/**
* Flatten a possibly multidimensional object to a one-dimensional one by converting all nested keys to dot notation
* @param obj - The object to flatten
* @param _d - Track the recursion depth to prevent overflow. (default: `0`)
* @returns A flattened object
*/
export function flattenObject(obj: object, _d?: number): object;
/**
* Obtain references to the parent classes of a certain class.
* @param cls - An ES6 Class definition
* @returns An array of parent Classes which the provided class extends
*/
export function getParentClasses(cls: AnyConstructor): Array<AnyConstructor>;
/**
* Get the URL route for a certain path which includes a path prefix, if one is set
* @param path - The Foundry URL path
* @returns The absolute URL path
*/
export function getRoute(path: string, { prefix }?: GetRouteOptions): string;
/** @internal */
type _GetRouteOptions = InexactPartial<{
/**
* A path prefix to apply
* @defaultValue {@linkcode globalThis.ROUTE_PREFIX}
*/
prefix: string | null;
}>;
export interface GetRouteOptions extends _GetRouteOptions {}
/**
* Learn the underlying data type of some variable. Supported identifiable types include:
* undefined, null, number, string, boolean, function, Array, Set, Map, Promise, Error,
* HTMLElement (client side only), Object (plain objects).
* If the type isn't identifiable, Unknown is returned.
* @param variable - A provided variable
* @returns The named type of the token
*/
export function getType(variable: unknown): DataType;
export type DataType =
| "Array"
| "Error"
| "HTMLElement"
| "Map"
| "Object"
| "Promise"
| "Set"
| "bigint"
| "boolean"
| "function"
| "null"
| "number"
| "string"
| "symbol"
| "undefined"
| "Unknown";
export type ExpandKey<K extends PropertyKey> = K extends `${infer First}.${infer Rest}`
? { [_ in First]: ExpandKey<Rest> }
: { [_ in K]: unknown };
/**
* A helper function which tests whether an object has a property or nested property given a string key.
* The string key supports the notation a.b.c which would return true if object[a][b][c] exists
* @param object - The object to traverse
* @param key - An object property with notation a.b.c
* @returns An indicator for whether the property exists
*/
export function hasProperty<K extends PropertyKey>(object: object, key: K): object is ExpandKey<K>;
/**
* A helper function which searches through an object to retrieve a value by a string key.
* The string key supports the notation a.b.c which would return object[a][b][c]
* @param object - The object to traverse
* @param key - An object property with notation a.b.c
* @returns The value of the found property
*/
export function getProperty(object: object, key: PropertyKey): unknown;
/**
* A helper function which searches through an object to assign a value using a string key
* This string key supports the notation a.b.c which would target object[a][b][c]
* @param object - The object to update
* @param key - The string key
* @param value - The value to be assigned
* @returns Whether the value was changed from its previous value
*
* @remarks - This type is likely to refuse to set a key that isn't known in the future.
*/
export function setProperty(object: object, key: PropertyKey, value: unknown): boolean;
/**
* A helper function which searches through an object to delete a value by a string key.
* The string key supports the notation a.b.c which would delete object[a][b][c]
* @param object - The object to traverse
* @param key - An object property with notation a.b.c
* @returns Was the property deleted?
*/
export function deleteProperty<T extends object>(object: T, key: DeletableDotKeys<T>): boolean;
/**
* Invert an object by assigning its values as keys and its keys as values.
* @param obj - The original object to invert
* @returns The inverted object with keys and values swapped
*/
export function invertObject<T extends InvertableObject>(obj: T): InvertObject<T>;
// Merging into this would be antithetical to its purpose.
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type InvertableObject = {
readonly [K: PropertyKey]: PropertyKey;
};
export type InvertObject<in out T extends InvertableObject> = {
-readonly [K in keyof T as T[K]]: K;
};
/**
* Return whether a target version (v1) is more advanced than some other reference version (v0).
* Supports either numeric or string version comparison with version parts separated by periods.
* @param v1 - The target version
* @param v0 - The reference version
* @returns Is v1 a more advanced version than v0?
*/
export function isNewerVersion(v1: number | string, v0: number | string): boolean;
/**
* Test whether a value is empty-like; either undefined or a content-less object.
* @param value - The value to test
* @returns Is the value empty-like?
*/
export function isEmpty(
value: undefined | null | unknown[] | object | Set<unknown> | Map<unknown, unknown> | NonNullish,
): boolean;
/**
* Update a source object by replacing its keys and values with those from a target object.
*
* @param original - The initial object which should be updated with values from the target
* @param other - A new object whose values should replace those in the source (default: `{}`)
* @param options - Additional options which configure the merge (default: `{}`)
* @param _d - A privately used parameter to track recursion depth. (default: `0`)
* @returns The original source object including updated, inserted, or overwritten records.
*
* @example Control how new keys and values are added
* ```typescript
* mergeObject({k1: "v1"}, {k2: "v2"}, {insertKeys: false}); // {k1: "v1"}
* mergeObject({k1: "v1"}, {k2: "v2"}, {insertKeys: true}); // {k1: "v1", k2: "v2"}
* mergeObject({k1: {i1: "v1"}}, {k1: {i2: "v2"}}, {insertValues: false}); // {k1: {i1: "v1"}}
* mergeObject({k1: {i1: "v1"}}, {k1: {i2: "v2"}}, {insertValues: true}); // {k1: {i1: "v1", i2: "v2"}}
* ```
*
* @example Control how existing data is overwritten
* ```typescript
* mergeObject({k1: "v1"}, {k1: "v2"}, {overwrite: true}); // {k1: "v2"}
* mergeObject({k1: "v1"}, {k1: "v2"}, {overwrite: false}); // {k1: "v1"}
* ```
*
* @example Control whether merges are performed recursively
* ```typescript
* mergeObject({k1: {i1: "v1"}}, {k1: {i2: "v2"}}, {recursive: false}); // {k1: {i2: "v2"}}
* mergeObject({k1: {i1: "v1"}}, {k1: {i2: "v2"}}, {recursive: true}); // {k1: {i1: "v1", i2: "v2"}}
* ```
*
* @example Deleting an existing object key
* ```typescript
* mergeObject({k1: "v1", k2: "v2"}, {"-=k1": null}, {performDeletions: true}); // {k2: "v2"}
* ```
*
* @example Explicitly replacing an inner object key
* ```js
* mergeObject({k1: {i1: "v1"}}, {"==k1": {i2: "v2"}}, {performDeletions: true}); // {k1: {i2: "v2"}}
* ```
*/
export function mergeObject<const T extends object, const U extends object, const M extends MergeObjectOptions>(
original: T,
other?: U,
options?: M,
_d?: number,
): MergeObject<T, U, M>;
export type MergeObject<T, U, M extends MergeObjectOptions> = UpdateInsert<
DeleteByObjectKeys<T, U, M>,
RemoveDeletingObjectKeys<U, M>,
M
>;
/** @internal */
type _MergeObjectOptions = InexactPartial<{
/**
* Control whether to insert new top-level objects into the resulting structure which do not previously exist in the original object.
* @defaultValue `true`
*/
insertKeys: boolean;
/**
* Control whether to insert new nested values into child objects in the resulting structure which did not previously exist in the original object.
* @defaultValue `true`
*/
insertValues: boolean;
/**
* Control whether to replace existing values in the source, or only merge values which do not already exist in the original object.
* @defaultValue `true`
*/
overwrite: boolean;
/**
* Control whether to merge inner-objects recursively (if true), or whether to simply replace inner objects with a provided new value.
* @defaultValue `true`
*/
recursive: boolean;
/**
* Control whether to apply updates to the original object in-place (if true), otherwise the original object is duplicated and the copy is merged.
* @defaultValue `true`
*/
inplace: boolean;
/**
* Control whether strict type checking requires that the value of a key in the other object must match the data type in the original data to be merged.
* @defaultValue `false`
*/
enforceTypes: boolean;
/**
* Control whether to perform deletions on the original object if deletion keys are present in the other object.
* @defaultValue `false`
*/
performDeletions: boolean; // TODO: implement this in the mergeObject return type
}>;
export interface MergeObjectOptions extends _MergeObjectOptions {}
/**
* Parse an S3 key to learn the bucket and the key prefix used for the request.
* @param key - A fully qualified key name or prefix path.
*/
export function parseS3URL(key: string): ParseS3URLReturn;
export interface ParseS3URLReturn {
/** @remarks `null` only if the passed URL fails {@linkcode URL.parseSafe} */
bucket: string | null;
/** @remarks `""` if the passed URL fails {@linkcode URL.parseSafe} */
keyPrefix: string;
}
/**
* Generate a random alphanumeric string ID of a given requested length using `crypto.getRandomValues()`.
* @param length - The length of the random string to generate, which must be at most 16384. (default: `16`)
* @returns A string containing random letters (A-Z, a-z) and numbers (0-9).
*/
export function randomID(length?: number): string;
/**
* Format a file size to an appropriate order of magnitude.
* @param size - The size in bytes.
* @param options - Additional options. (default: `{}`)
*/
export function formatFileSize(size: number, options?: FormatFileSizeOptions): string;
/** @internal */
type _FormatFileSizeOptions = InexactPartial<{
/**
* The number of decimal places to round to.
* @defaultValue `2`
*/
decimalPlaces: number;
/**
* The base to use. In base 10 a kilobyte is 1000 bytes. In base 2 it is 1024 bytes.
* @defaultValue `10`
* @remarks Foundry only actually checks if the base is `2` or not; all other values are treated as `10`
*/
base: 2 | 10;
}>;
export interface FormatFileSizeOptions extends _FormatFileSizeOptions {}
export interface ResolvedUUID {
/**
* The original UUID.
*/
uuid: string;
/**
* The type of Document referenced. Legacy compendium UUIDs will not populate this field if the compendium is not active in the World.
* @remarks This property is always present, the above caveat simply means it might be `undefined`
*/
type: Document.Type | undefined;
/**
* The ID of the Document referenced.
*/
id: string;
/**
* The primary Document type of this UUID. Only present if the Document is embedded.
* @remarks This property is always present, the above caveat simply means it might be `undefined`
*/
primaryType: CONST.PRIMARY_DOCUMENT_TYPES | undefined;
/**
* The primary Document ID of this UUID. Only present if the Document is embedded.
* @remarks This property is always present, the above caveat simply means it might be `undefined`
*/
primaryId: string | undefined;
/**
* The collection that the primary Document belongs to.
*/
collection: foundry.documents.abstract.DocumentCollection.Any | undefined;
/**
* Additional Embedded Document parts.
*/
embedded: string[];
/**
* Either the document type or the parent type. Retained for backwards compatibility.
*/
documentType: Document.Type | undefined;
/**
* Either the document id or the parent id. Retained for backwards compatibility.
*/
documentId: string | undefined;
}
/**
* Parse a UUID into its constituent parts, identifying the type and ID of the referenced document.
* The ResolvedUUID result also identifies a "primary" document which is a root-level document either in the game
* World or in a Compendium pack which is a parent of the referenced document.
* @param uuid - The UUID to parse.
* @param options - Options to configure parsing behavior.
* @returns Returns, if possible, the Collection, Document Type, and Document ID to resolve the parent document, as well as the remaining Embedded Document parts, if any.
*/
export function parseUuid(uuid: string, options?: ParseUUIDOptions): ResolvedUUID;
/** @internal */
type _ParseUUIDOptions = InexactPartial<{
/**
* A document to resolve relative UUIDs against.
*/
relative: Document.Any;
}>;
export interface ParseUUIDOptions extends _ParseUUIDOptions {}
/**
* Escape the given unescaped string.
*
* Escaped strings are safe to use inside inner HTML of most tags and in most quoted HTML attributes.
* They are not NOT safe to use in `<script>` tags, unquoted attributes, `href`, `onmouseover`, and similar.
* They must be unescaped first if they are used inside a context that would escape them.
*
* Handles only `&`, `<`, `>`, `"`, and `'`.
* @see {@linkcode foundry.utils.unescapeHTML}
* @param value - An unescaped string
* @returns The escaped string
* @privateRemarks Foundry types `value` as `string|any`, as the method passes it through `String()`, but accounting for that seems counterproductive
*/
export function escapeHTML(value: string): string;
/**
* Unescape the given escaped string.
*
* Handles only `&`, `<`, `>`, `"`, and `'`.
* @see {@linkcode foundry.utils.escapeHTML}
* @param value - An escaped string
* @returns The escaped string
*/
export function unescapeHTML(value: string): string;
/**
* Build a Universally Unique Identifier (uuid) from possibly limited data. An attempt will be made to resolve omitted
* components, but an identifier and at least one of documentName, parent, and pack are required.
* @param context - Data for building the uuid
* @returns A well-formed Document uuid unless one is unable to be created
*/
export function buildUuid(context?: BuildUUIDContext): string | null;
/** @internal */
type _BuildUUIDContext = InexactPartial<{
/**
* The document name (or type)
* @remarks At least one of `documentName`, `parent`, or `pack` *must* be provided or `buildUuid` will return `null`
*/
documentName: Document.Type;
/**
* The document's parent, if any
* @remarks At least one of `documentName`, `parent`, or `pack` *must* be provided or `buildUuid` will return `null`
*/
parent: Document.Any | null;
/**
* The document's compendium pack, if applicable
* @remarks At least one of `documentName`, `parent`, or `pack` *must* be provided or `buildUuid` will return `null`
*/
pack: string | null;
}>;
export interface BuildUUIDContext extends _BuildUUIDContext {
/** The identifier of the document */
id: string;
}
/**
* Internal Helper for {@linkcode Duplicated}. A union type of all types that do not have a JSON representation.
*
* @internal
*/
type NonStringifiable = undefined | AnyFunction | AnyConstructor | symbol;
/**
* Internal helper for {@linkcode InnerDuplicated}. Maps the properties of `T` to their duplicated types.
*
* @template T - The object type that should have its properties mapped.
* @internal
*/
type MapToInnerDuplicated<T extends object> = { [k in keyof T]: InnerDuplicated<T[k]> };
/**
* Omit properties of `T` which are of type `U`.
*
* @template T - Object type from which properties will be omitted.
* @template U - Properties of this type will be omitted.
* @internal
*/
type OmitOfType<T extends object, U> = { [k in keyof T as T[k] extends U ? never : k]: T[k] };
/**
* Internal helper type for {@linkcode Duplicated}. It is the main part of the implementation, which does the recursion.
*
* @template T - Type currently being converted.
* @internal
*/
// prettier-ignore
type InnerDuplicated<T> = T extends { toJSON(): infer U }
? U extends Array<unknown>
? InnerDuplicated<U>
: U extends object
? InnerDuplicated<Omit<U, "toJSON">>
: InnerDuplicated<U>
: T extends NonStringifiable
? undefined
: T extends Array<unknown>
? MapToInnerDuplicated<MapTypeToType<T, NonStringifiable, null>>
: T extends object
? MapToInnerDuplicated<
OmitAssignableFromType<MapTypeToType<T, NonStringifiable, undefined>, undefined> &
Partial<OmitOfType<OmitNotAssignableFromType<MapTypeToType<T, NonStringifiable, undefined>, undefined>, undefined>>
>
: T;
/**
* If T extends `U`, the resulting type is `R`, otherwise it is `T`.
*
* @template T - Original type.
* @template U - Only convert types of this type.
* @template R - Adjust to this type.
* @internal
*/
type TypeToType<T, U, R> = T extends U ? R : T;
/**
* Map the types of properties of `T` to `R` if they are of type `U`.
*
* @template T - Object type that will have its properties' types adjusted.
* @template U - Adjust the types of properties of this type.
* @template R - Type that properties' types will be adjusted to.
* @internal
*/
type MapTypeToType<T, U, R> = { [k in keyof T]: TypeToType<T[k], U, R> };
/**
* Omit properties of `T` which are assignable from `U`.
*
* @template T - Object type that will have its properties omitted.
* @template U - Properties with types that are assignable from this type will be omitted.
* @internal
*/
type OmitAssignableFromType<T extends object, U> = { [k in keyof T as U extends T[k] ? never : k]: T[k] };
/**
* Omit properties of `T` which are not assignable from `U`.
*
* @template T - Object type that will have its properties omitted.
* @template U - Properties with types that are not assignable from this type will be omitted.
* @internal
*/
type OmitNotAssignableFromType<T extends object, U> = { [k in keyof T as U extends T[k] ? k : never]: T[k] };
type OmitByValue<T, ValueType> = { -readonly [Key in keyof T as T[Key] extends ValueType ? never : Key]: T[Key] };
type RemoveNever<T> = OmitByValue<T, never>;
type PropWithMinus<K> = K extends string ? `-=${K}` : never;
type DeleteByObjectKeys<T, U, M extends MergeObjectOptions> = M["performDeletions"] extends true
? RemoveNever<{
-readonly [K in keyof T]: PropWithMinus<K> extends keyof U
? U[PropWithMinus<K>] extends null
? never
: T[K]
: T[K];
}>
: T;
type RemoveDeletingObjectKeys<T, M extends MergeObjectOptions> = M["performDeletions"] extends true
? RemoveNever<{
-readonly [K in keyof T]: K extends string
? Capitalize<K> extends K
? T[K] extends null
? never
: T[K]
: T[K]
: T[K];
}>
: T;
type NonObject = number | string | boolean | bigint | symbol | null | undefined;
type MergeObjectProperty<T, U, M extends MergeObjectOptions> = U extends NonObject
? U
: T extends AnyArray
? { -readonly [K in keyof U]: U[K] }
: T extends Record<string, any>
? U extends Record<string, any>
? M extends { recursive: false }
? { -readonly [K in keyof U]: U[K] }
: MergeObject<
T,
U,
Omit<M, "insertKeys" | "performDeletions"> & {
insertKeys: M["insertValues"];
performDeletions: M["performDeletions"] extends true ? true : false;
}
>
: U
: U;
type UpdateKeys<T, U, M extends MergeObjectOptions> = M extends { overwrite: false }
? { -readonly [K in keyof T]: T[K] }
: { -readonly [K in keyof T]: K extends keyof U ? MergeObjectProperty<T[K], U[K], M> : T[K] };
type InsertKeys<T, U> = { -readonly [K in keyof T]: T[K] } & { -readonly [K in keyof U as Exclude<K, keyof T>]: U[K] };
type UpdateInsert<T, U, M extends MergeObjectOptions> = M extends { insertKeys: false }
? UpdateKeys<T, U, M>
: InsertKeys<UpdateKeys<T, U, M>, U>;