fast-tree-builder
Version:
Easily construct highly customizable bi-directional tree structures from iterable data.
124 lines (123 loc) • 5.63 kB
text/typescript
type TreeNode<TValue, TValueKey extends PropertyKey | false, TParentKey extends PropertyKey | false, TChildrenKey extends PropertyKey, TDepthKey extends PropertyKey | false, TIncludeEmptyChildrenArray extends boolean> = (TValueKey extends false ? Omit<TValue, Exclude<TParentKey, false> | TChildrenKey | Exclude<TDepthKey, false>> : {
[k in Exclude<TValueKey, false>]: TValue;
}) & (TParentKey extends false ? {} : {
[k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>;
}) & (TIncludeEmptyChildrenArray extends true ? {
[k in TChildrenKey]: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
} : {
[k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
}) & (TDepthKey extends false ? {} : {
[k in Exclude<TDepthKey, false>]: number;
});
type AccessorReturnType<O, P extends (keyof O) | ((item: O) => any)> = P extends ((item: O) => infer R) ? R : P extends (keyof O) ? O[P] : never;
type ObjectKeysOfIterableProperties<T> = 0 extends (1 & T) ? PropertyKey : {
[K in keyof T]: T[K] extends (Iterable<unknown> & object) | null | undefined ? K : never;
}[keyof T];
declare function buildTree<TIdAccessor extends NoInfer<keyof TInputValue> | ((item: NoInfer<TInputValue>) => unknown), TValueKey extends PropertyKey | false = 'value', TParentKey extends PropertyKey | false = 'parent', TChildrenKey extends PropertyKey = 'children', TDepthKey extends PropertyKey | false = false, TInputValue extends (TValueKey extends false ? object : TIdAccessor extends PropertyKey ? object : unknown) = any, TResolvedValue extends (TValueKey extends false ? object : unknown) = TInputValue, TIncludeEmptyChildrenArray extends boolean = false>(items: Iterable<TInputValue>, options: {
/**
* A string key or function used to get the item's unique identifier.
*/
id: TIdAccessor;
/**
* Function to transform an item to a custom value stored in the node.
*/
valueResolver?: {
(item: NoInfer<TInputValue>): TResolvedValue;
};
/**
* Key where the item is stored in the output node.
*
* Set to `false` to merge the item's properties directly into the node (shallow copy).
*
* Defaults to `'value'`.
*/
valueKey?: TValueKey;
/**
* Key where the node's parent reference is stored in the output node.
*
* Set to `false` to omit parent links.
*
* Defaults to `'parent'`.
*/
parentKey?: TParentKey;
/**
* Key where the node's children are stored in the output node.
*
* Defaults to `'children'`.
*/
childrenKey?: TChildrenKey;
/**
* Key where the node's depth is stored in the output node.
* Root nodes have a depth of 0.
*
* Set to `false` to omit depth values.
*
* Setting this enables validateTree implicitly, as depth calculation requires full tree validation.
* Both operations share the same traversal logic so the additional tree validation is not an overhead.
*
* Defaults to `false`.
*/
depthKey?: TDepthKey;
/**
* Leaf nodes will include an empty children array when this is set to `true`.
* Otherwise they are left as `undefined`.
*
* This ensures you can loop over every node child list without checking its existence.
*
* Defaults to `false`.
*/
includeEmptyChildrenArray?: TIncludeEmptyChildrenArray;
/**
* Validates that the final structure forms a tree.
*
* Ensures:
* - No cycles
* - No node reachable through multiple paths
*
* Throws if the structure is not a proper tree.
*
* Defaults to `false`.
*/
validateTree?: boolean;
/**
* When true, verifies all parentId or childIds resolve to real items.
* Only `null` and `undefined` are acceptable as parent id for root nodes in parentId mode.
* Every item in the children list must resolve to a real item in childIds mode.
*
* Errors are thrown on invalid references.
*
* Defaults to `false`.
*/
validateReferences?: boolean;
} & ({
/**
* A string key or function used to get the item's parent identifier.
*
* Either `parentId` or `childIds` must be provided.
*/
parentId?: never;
/**
* A string key or function to retrieve a list of child identifiers from an item.
*
* Either `parentId` or `childIds` must be provided.
*/
childIds: NoInfer<ObjectKeysOfIterableProperties<TInputValue>> | ((item: NoInfer<TInputValue>) => (Iterable<unknown> & object) | null | undefined);
} | {
/**
* A string key or function used to get the item's parent identifier.
*
* Either `parentId` or `childIds` must be provided.
*/
parentId: NoInfer<keyof TInputValue> | ((item: NoInfer<TInputValue>) => unknown);
/**
* A string key or function to retrieve a list of child identifiers from an item.
*
* Either `parentId` or `childIds` must be provided.
*/
childIds?: never;
})): {
roots: TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>>;
};
declare const _default: { default: typeof buildTree };
export = _default;