Skip to content

Commit 6ff6b71

Browse files
authored
chore: update dependencies and improve filter function checks (#18)
- Bump devDependencies: @eslint/js to 10.0.1, @types/node to 25.5.0, prettier to 3.8.1, and typescript-eslint to 8.58.0. - Refactor filter function checks in filter.test.ts to handle undefined values gracefully. - Update tsconfig.json to set moduleResolution to "bundler" and specify rootDir as "lib".
1 parent 1b9c2ae commit 6ff6b71

9 files changed

Lines changed: 672 additions & 1027 deletions

File tree

README.md

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
A utility library designed to simplify working with deeply nested objects in TypeScript. This is intended to be used when you work with dynamic keys or when you are not sure if the property exists. It provides a set of functions so you don't have to deal with the complexity of checking for the existence of properties at each level of the object.
1+
A utility library designed to simplify working with deeply nested objects in TypeScript.
2+
This is intended to be used when you work with dynamic keys or when you are not sure if the property exists.
3+
It provides a set of functions so you don't have to deal with the complexity of checking for the existence of properties at each level of the object.
24

35
## Features
46

@@ -17,9 +19,11 @@ npm install @nuc-lib/deep-key
1719

1820
### Accessing Nested Properties
1921

20-
The `getKeyValue` utility lets you access nested properties using a dot notation string. If the property does not exist, it returns `undefined` instead of throwing an error.
22+
The `getKeyValue` utility lets you access nested properties using a dot notation string.
23+
If the property does not exist, it returns `undefined` instead of throwing an error.
2124

22-
The key is a string that represents the path to the property you want to access. The path is defined using dot notation, where each level of the object is separated by a dot.
25+
The key is a string that represents the path to the property you want to access.
26+
The path is defined using dot notation, where each level of the object is separated by a dot.
2327

2428
```javascript
2529
import { getKeyValue } from '@nuc-lib/deep-key';
@@ -43,16 +47,14 @@ const guy = {
4347
getKeyValue({ object: guy, key: 'personalInfo.name' }); // 'John Doe'
4448
getKeyValue({ object: guy, key: 'personalInfo.age' }); // 12
4549
getKeyValue({ object: guy, key: 'personalInfo.city' }); // 'New York'
46-
getKeyValue({ object: guy, key: 'personalInfo.active' }); // undefined -> no error thrown
4750
```
4851

4952
For arrays there are two ways to access the values:
5053

5154
- **Getting an element in a specific index.**
5255

5356
```javascript
54-
getKeyValue({ object: guy, key: 'contacts.0' });
55-
// { name: 'Jane Doe', email: 'afk@example.com' }
57+
getKeyValue({ object: guy, key: 'contacts.0' }); // { name: 'Jane Doe', email: 'afk@example.com' }
5658
getKeyValue({ object: guy, key: 'contacts.0.name' }); // 'Jane Doe'
5759
getKeyValue({ object: guy, key: 'contacts.0.email' }); // 'afk@example.com'
5860
```
@@ -69,7 +71,8 @@ getKeyValue({ object: guy, key: '[contacts].invalidKey' });
6971

7072
### Filtering By Nested Properties
7173

72-
The `filterByKeyValue` utility allows you to filter an array of objects based on a nested property. It returns a new array containing only the objects that match the specified key and value.
74+
The `filterByKeyValue` utility allows you to filter an array of objects based on a nested property.
75+
It returns a new array containing only the objects that match the specified key and value.
7376

7477
```javascript
7578
import { filterByKeyValue } from '@nuc-lib/deep-key';
@@ -111,7 +114,6 @@ filterByKeyValue({
111114
filter: (value) => value === 25
112115
});
113116
// [ { id: 1, name: 'John Doe', age: 25, parentIds: [ 1, 2 ], address: { city: 'Houston', zip: '10001' } } ]
114-
115117
filterByKeyValue({
116118
array: people,
117119
key: 'address.city',
@@ -121,12 +123,6 @@ filterByKeyValue({
121123
// { id: 1, name: 'John Doe', age: 25, parentIds: [ 1, 2 ], address: { city: 'Houston', zip: '10001' } },
122124
// { id: 4, name: 'Bob Johnson', age: 28, parentIds: [7, 8], address: { city: 'Houston', zip: '77001' } }
123125
// ]
124-
filterByKeyValue({
125-
array: people,
126-
key: 'address',
127-
filter: (value) => value === 'Houston'
128-
});
129-
// [] -> no error thrown
130126
filterByKeyValue({
131127
array: people,
132128
key: 'age',
@@ -149,7 +145,8 @@ filterByKeyValue({
149145

150146
### Sorting By Nested Properties
151147

152-
The `sortByKeyValue` utility allows you to sort an array of objects based on a nested property. It returns a new array sorted in ascending order by default.
148+
The `sortByKeyValue` utility allows you to sort an array of objects based on a nested property.
149+
It returns a new array sorted in ascending order by default.
153150

154151
```javascript
155152
import { sortByKeyValue } from '@nuc-lib/deep-key';

lib/types.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,52 @@ export type DeepKeyOf<T extends TObject> = keyof {
4040
: `${K}.${DeepKeyOf<T[K]>}`
4141
: never]: unknown;
4242
};
43+
44+
/**
45+
* Represents the type of a value at a specific key in an object, including nested keys.
46+
* This type is useful for creating dynamic forms or validating object structures.
47+
*/
48+
export type DeepTypeOfKey<T extends TObject, K extends string> =
49+
// array.0.x
50+
K extends `${infer Key}.${number}.${infer Rest}`
51+
? Key extends keyof T
52+
? T[Key] extends Array<infer U>
53+
? U extends TObject
54+
? DeepTypeOfKey<U, Rest>
55+
: never
56+
: never
57+
: never
58+
: // array.0
59+
K extends `${infer Key}.${number}`
60+
? Key extends keyof T
61+
? T[Key] extends Array<infer U>
62+
? U
63+
: never
64+
: never
65+
: // [array].x
66+
K extends `[${infer Key}].${infer Rest}`
67+
? Key extends keyof T
68+
? T[Key] extends Array<infer U>
69+
? U extends TObject
70+
? DeepTypeOfKey<U, Rest>
71+
: never
72+
: never
73+
: never
74+
: // [array]
75+
K extends `[${infer Key}]`
76+
? Key extends keyof T
77+
? T[Key] extends Array<infer U>
78+
? U
79+
: never
80+
: never
81+
: // object.key
82+
K extends `${infer Key}.${infer Rest}`
83+
? Key extends keyof T
84+
? T[Key] extends TObject
85+
? DeepTypeOfKey<T[Key], Rest>
86+
: never
87+
: never
88+
: // direct key
89+
K extends keyof T
90+
? T[K]
91+
: never;

lib/utils/filter.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { getKeyValue } from './value';
22

3-
import type { DeepKeyOf, TObject } from '../types';
3+
import type { DeepKeyOf, DeepTypeOfKey, TObject } from '../types';
44

5-
type FilterParams<T extends TObject> = {
5+
type FilterParams<T extends TObject, K extends DeepKeyOf<T>> = {
66
array: T[];
7-
key: DeepKeyOf<T>;
8-
filter: (value: unknown) => boolean;
7+
key: K;
8+
filter: (value: DeepTypeOfKey<T, K> | undefined) => boolean;
99
};
1010

1111
/**
@@ -14,18 +14,13 @@ type FilterParams<T extends TObject> = {
1414
* @param params - The parameters for filtering, including the array of objects, the key to filter by, and the filter function.
1515
* @returns A new array with the filtered objects.
1616
*/
17-
export const filterByKeyValue = <T extends TObject>({
17+
export const filterByKeyValue = <T extends TObject, K extends DeepKeyOf<T>>({
1818
array,
1919
filter,
2020
key
21-
}: FilterParams<T>) => {
21+
}: FilterParams<T, K>): T[] => {
2222
return array.filter((item) => {
2323
const itemKeyValue = getKeyValue({ object: item, key });
24-
25-
// Early return if itemKeyValue is undefined
26-
if (!itemKeyValue) {
27-
return false;
28-
}
2924
return filter(itemKeyValue);
3025
});
3126
};

lib/utils/sort.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const sortByKeyValue = <T extends TObject>({
1717
array,
1818
key,
1919
order = 'ASC'
20-
}: SortParams<T>) => {
20+
}: SortParams<T>): T[] => {
2121
const sortedArray = array.toSorted((a, b) => {
2222
const aValue = getKeyValue({ object: a, key });
2323
const bValue = getKeyValue({ object: b, key });

lib/utils/value.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import type { KeyOf, TObject } from '../types';
2+
import type { DeepTypeOfKey, KeyOf, TObject } from '../types';
33

4-
type Params<T extends TObject> = {
4+
type Params<T extends TObject, K extends KeyOf<T>> = {
55
object: T;
6-
key: KeyOf<T>;
6+
key: K;
77
};
88

99
/**
1010
* Gets the value of a key in an object, including nested keys.
1111
* @param params - The parameters for getting the key value, including the object and the key
12-
* The key is represented as a string, with nested keys separated by dots (e.g. 'key1.key2.key3').
12+
* The key is represented as a string, with nested keys separated by dots (e.g. `key1.key2.key3`).
1313
* @returns The value of the key in the object.
1414
*/
15-
export const getKeyValue = <T extends TObject>({
15+
export const getKeyValue = <T extends TObject, K extends KeyOf<T>>({
1616
object,
1717
key
18-
}: Params<T>): unknown => {
18+
}: Params<T, K>): DeepTypeOfKey<T, K> | undefined => {
1919
const subKeys = String(key)
2020
.split('.')
2121
.map((key) => key.replace(/[[\]]/g, ''));
2222
// Initialize the value with the object
2323
let value: any = object;
2424

2525
// If the object is an array, return undefined
26-
// This is to prevent the function from returning an array empty objects
26+
// This is to prevent the function from returning an array of empty objects
2727
if (Array.isArray(object)) {
2828
return undefined;
2929
}

0 commit comments

Comments
 (0)