@etsoo/shared 1.0.76 → 1.0.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -132,6 +132,7 @@ String and other related utilities
132
132
  |Name|Description|
133
133
  |---:|---|
134
134
  |charsToNumber|Base64 chars to number|
135
+ |equals|Two values equal|
135
136
  |formatInitial|Format inital character to lower case or upper case|
136
137
  |formatString|Format string with parameters|
137
138
  |getDataChanges|Get data changed fields with input data updated|
@@ -142,6 +143,8 @@ String and other related utilities
142
143
  |newGUID|Create a GUID|
143
144
  |numberToChars|Number to base64 chars|
144
145
  |objectEqual|Test two objects are equal or not|
146
+ |objectKeys|Get two object's unqiue properties|
147
+ |objectUpdated|Get the new object's updated fields contrast to the previous object|
145
148
  |parseString|Parse string (JSON) to specific type|
146
149
  |setLabels|Set source with new labels|
147
150
  |snakeNameToWord|Snake name to works, 'snake_name' to 'Snake Name'|
@@ -7,7 +7,7 @@ test('Tests for all', () => {
7
7
  StorageUtils.setSessionData('number', 3.14);
8
8
  StorageUtils.setSessionData('test', { id: 123, name: 'test' });
9
9
 
10
- expect(StorageUtils.getSessionData('string', '')).toBe('test');
10
+ expect(StorageUtils.getSessionData<string>('string')).toBe('test');
11
11
  expect(StorageUtils.getSessionData('boolean', false)).toBe(true);
12
12
  expect(StorageUtils.getSessionData('number', 0)).toBe(3.14);
13
13
  expect(StorageUtils.getSessionData('test', {})).toHaveProperty('id', 123);
@@ -9,7 +9,8 @@ test('Tests for getDataChanges', () => {
9
9
  price: '6.0',
10
10
  amount: '',
11
11
  enabled: true,
12
- value: undefined
12
+ value: undefined,
13
+ ids: [1, 2]
13
14
  };
14
15
  const initData = {
15
16
  id: 1,
@@ -18,7 +19,8 @@ test('Tests for getDataChanges', () => {
18
19
  brand: 'ETSOO',
19
20
  price: 6,
20
21
  amount: 0,
21
- enabled: true
22
+ enabled: true,
23
+ ids: [1, 2]
22
24
  };
23
25
  const fields = Utils.getDataChanges(input, initData);
24
26
  expect(fields).toStrictEqual(['gender', 'brand', 'amount']);
@@ -82,17 +84,27 @@ test('Tests for removeNonLetters', () => {
82
84
  });
83
85
 
84
86
  test('Tests for objectEqual', () => {
85
- const obj1 = { a: 1, b: 'abc', c: true, d: null };
86
- const obj2 = { a: '1', b: 'abc', c: true };
87
+ const obj1 = { a: 1, b: 'abc', c: true, d: null, f: [1, 2] };
88
+ const obj2 = { a: '1', b: 'abc', c: true, f: [1, 2] };
87
89
  expect(Utils.objectEqual(obj1, obj2)).toBeFalsy();
88
90
  expect(Utils.objectEqual(obj1, obj2, [], 0)).toBeTruthy();
89
91
  expect(Utils.objectEqual(obj1, obj2, ['a'])).toBeTruthy();
90
92
  expect(Utils.objectEqual(obj1, obj2, ['a'], 2)).toBeFalsy();
91
93
  });
92
94
 
95
+ test('Tests for objectUpdated', () => {
96
+ const objPrev = { a: 1, b: 'abc', c: true, d: null, f: [1, 2] };
97
+ const objNew = { a: 2, b: 'abc', d: new Date(), f: [1, 2, 3] };
98
+ const fields = Utils.objectUpdated(objNew, objPrev, ['d']);
99
+ expect(fields.sort()).toStrictEqual(['a', 'c', 'f']);
100
+ });
101
+
93
102
  test('Tests for parseString', () => {
94
- expect(Utils.parseString('test', '')).toBe('test');
103
+ expect(Utils.parseString<string>('test')).toBe('test');
95
104
  expect(Utils.parseString('true', false)).toBe(true);
105
+ expect(Utils.parseString('', false)).toBeFalsy();
106
+ expect(Utils.parseString<boolean>('')).toBeUndefined();
107
+ expect(Utils.parseString<number>(undefined)).toBeUndefined();
96
108
  expect(Utils.parseString('3.14', 0)).toBe(3.14);
97
109
  expect(Utils.parseString('2021/4/13', new Date())).toStrictEqual(
98
110
  new Date('2021/4/13')
@@ -20,7 +20,7 @@ export declare namespace StorageUtils {
20
20
  * @param key Key name
21
21
  * @param defaultValue Default value
22
22
  */
23
- function getLocalData<T>(key: string, defaultValue: T): T;
23
+ function getLocalData<T, M = T | undefined>(key: string, defaultValue?: M): M extends T ? M : undefined;
24
24
  /**
25
25
  * Get local storage object data
26
26
  * @param key Key name
@@ -30,7 +30,7 @@ export declare namespace StorageUtils {
30
30
  * Get session storage data
31
31
  * @param key Key name
32
32
  */
33
- function getSessionData<T>(key: string, defaultValue: T): T;
33
+ function getSessionData<T, M = T | undefined>(key: string, defaultValue?: M): M extends T ? M : undefined;
34
34
  /**
35
35
  * Get session storage object data
36
36
  * @param key Key name
@@ -41,6 +41,13 @@ export declare namespace Utils {
41
41
  * @returns Number
42
42
  */
43
43
  function charsToNumber(base64Chars: string): number;
44
+ /**
45
+ * Two values equal
46
+ * @param v1 Value 1
47
+ * @param v2 Value 2
48
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
49
+ */
50
+ function equals(v1: unknown, v2: unknown, strict?: number): boolean;
44
51
  /**
45
52
  * Format inital character to lower case or upper case
46
53
  * @param input Input string
@@ -111,12 +118,30 @@ export declare namespace Utils {
111
118
  */
112
119
  function objectEqual(obj1: {}, obj2: {}, ignoreFields?: string[], strict?: number): boolean;
113
120
  /**
114
- * Parse string (JSON) to specific type
121
+ * Get two object's unqiue properties
122
+ * @param obj1 Object 1
123
+ * @param obj2 Object 2
124
+ * @param ignoreFields Ignored fields
125
+ * @returns Unique properties
126
+ */
127
+ function objectKeys(obj1: {}, obj2: {}, ignoreFields?: string[]): Set<string>;
128
+ /**
129
+ * Get the new object's updated fields contrast to the previous object
130
+ * @param objNew New object
131
+ * @param objPre Previous object
132
+ * @param ignoreFields Ignored fields
133
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
134
+ * @returns Updated fields
135
+ */
136
+ function objectUpdated(objNew: {}, objPrev: {}, ignoreFields?: string[], strict?: number): string[];
137
+ /**
138
+ * Parse string (JSON) to specific type, no type conversion
139
+ * For type conversion, please use DataTypes.convert
115
140
  * @param input Input string
116
141
  * @param defaultValue Default value
117
142
  * @returns Parsed value
118
143
  */
119
- function parseString<T>(input: string | undefined | null, defaultValue: T): T;
144
+ function parseString<T, M = T | undefined>(input: string | undefined | null, defaultValue?: M): M extends T ? M : undefined;
120
145
  /**
121
146
  * Remove non letters
122
147
  * @param input Input string
package/lib/cjs/Utils.js CHANGED
@@ -43,6 +43,29 @@ var Utils;
43
43
  }, 0);
44
44
  }
45
45
  Utils.charsToNumber = charsToNumber;
46
+ /**
47
+ * Two values equal
48
+ * @param v1 Value 1
49
+ * @param v2 Value 2
50
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
51
+ */
52
+ function equals(v1, v2, strict = 1) {
53
+ // Null and undefined case
54
+ if (v1 == null || v2 == null) {
55
+ if (strict <= 1 && v1 == v2)
56
+ return true;
57
+ return v1 === v2;
58
+ }
59
+ // For array and object
60
+ if (typeof v1 === 'object')
61
+ return JSON.stringify(v1) === JSON.stringify(v2);
62
+ // 1 and '1' case
63
+ if (strict === 0)
64
+ return v1 == v2;
65
+ // Strict equal
66
+ return v1 === v2;
67
+ }
68
+ Utils.equals = equals;
46
69
  /**
47
70
  * Format inital character to lower case or upper case
48
71
  * @param input Input string
@@ -85,7 +108,7 @@ var Utils;
85
108
  }
86
109
  if (initValue != null) {
87
110
  const newValue = DataTypes_1.DataTypes.convert(value, initValue);
88
- if (newValue === initValue) {
111
+ if (Utils.equals(newValue, initValue)) {
89
112
  Reflect.deleteProperty(input, key);
90
113
  return;
91
114
  }
@@ -193,37 +216,66 @@ var Utils;
193
216
  * @returns Result
194
217
  */
195
218
  function objectEqual(obj1, obj2, ignoreFields = [], strict = 1) {
196
- // Keys
197
- const keys = new Set([
198
- ...Object.keys(obj1).filter((item) => !ignoreFields.includes(item)),
199
- ...Object.keys(obj2).filter((item) => !ignoreFields.includes(item))
200
- ]);
219
+ // Unique keys
220
+ const keys = Utils.objectKeys(obj1, obj2, ignoreFields);
201
221
  for (const key of keys) {
202
222
  // Values
203
223
  const v1 = Reflect.get(obj1, key);
204
224
  const v2 = Reflect.get(obj2, key);
205
- // Null and undefined case
206
- if (v1 == null && v2 == null && strict <= 1)
207
- continue;
208
- // 1 and '1' case
209
- if (strict === 0 && v1 == v2)
210
- continue;
211
- // Strict equal
212
- if (v1 !== v2)
225
+ if (!Utils.equals(v1, v2, strict))
213
226
  return false;
214
227
  }
215
228
  return true;
216
229
  }
217
230
  Utils.objectEqual = objectEqual;
218
231
  /**
219
- * Parse string (JSON) to specific type
232
+ * Get two object's unqiue properties
233
+ * @param obj1 Object 1
234
+ * @param obj2 Object 2
235
+ * @param ignoreFields Ignored fields
236
+ * @returns Unique properties
237
+ */
238
+ function objectKeys(obj1, obj2, ignoreFields = []) {
239
+ // All keys
240
+ const allKeys = [...Object.keys(obj1), ...Object.keys(obj2)].filter((item) => !ignoreFields.includes(item));
241
+ // Unique keys
242
+ return new Set(allKeys);
243
+ }
244
+ Utils.objectKeys = objectKeys;
245
+ /**
246
+ * Get the new object's updated fields contrast to the previous object
247
+ * @param objNew New object
248
+ * @param objPre Previous object
249
+ * @param ignoreFields Ignored fields
250
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
251
+ * @returns Updated fields
252
+ */
253
+ function objectUpdated(objNew, objPrev, ignoreFields = [], strict = 1) {
254
+ // Fields
255
+ const fields = [];
256
+ // Unique keys
257
+ const keys = Utils.objectKeys(objNew, objPrev, ignoreFields);
258
+ for (const key of keys) {
259
+ // Values
260
+ const vNew = Reflect.get(objNew, key);
261
+ const vPrev = Reflect.get(objPrev, key);
262
+ if (!Utils.equals(vNew, vPrev, strict)) {
263
+ fields.push(key);
264
+ }
265
+ }
266
+ return fields;
267
+ }
268
+ Utils.objectUpdated = objectUpdated;
269
+ /**
270
+ * Parse string (JSON) to specific type, no type conversion
271
+ * For type conversion, please use DataTypes.convert
220
272
  * @param input Input string
221
273
  * @param defaultValue Default value
222
274
  * @returns Parsed value
223
275
  */
224
276
  function parseString(input, defaultValue) {
225
- // Undefined case, return default value
226
- if (input == null)
277
+ // Undefined and empty case, return default value
278
+ if (input == null || input === '')
227
279
  return defaultValue;
228
280
  // String
229
281
  if (typeof defaultValue === 'string')
@@ -241,16 +293,11 @@ var Utils;
241
293
  // Return
242
294
  return json;
243
295
  }
244
- catch (e) {
245
- console.log('Utils.parseString error', e);
296
+ catch {
297
+ if (defaultValue == null)
298
+ return input;
246
299
  return defaultValue;
247
300
  }
248
- /*
249
- finally part will still return the boolean value
250
- finally {
251
- return defaultValue
252
- }
253
- */
254
301
  }
255
302
  Utils.parseString = parseString;
256
303
  /**
@@ -20,7 +20,7 @@ export declare namespace StorageUtils {
20
20
  * @param key Key name
21
21
  * @param defaultValue Default value
22
22
  */
23
- function getLocalData<T>(key: string, defaultValue: T): T;
23
+ function getLocalData<T, M = T | undefined>(key: string, defaultValue?: M): M extends T ? M : undefined;
24
24
  /**
25
25
  * Get local storage object data
26
26
  * @param key Key name
@@ -30,7 +30,7 @@ export declare namespace StorageUtils {
30
30
  * Get session storage data
31
31
  * @param key Key name
32
32
  */
33
- function getSessionData<T>(key: string, defaultValue: T): T;
33
+ function getSessionData<T, M = T | undefined>(key: string, defaultValue?: M): M extends T ? M : undefined;
34
34
  /**
35
35
  * Get session storage object data
36
36
  * @param key Key name
@@ -41,6 +41,13 @@ export declare namespace Utils {
41
41
  * @returns Number
42
42
  */
43
43
  function charsToNumber(base64Chars: string): number;
44
+ /**
45
+ * Two values equal
46
+ * @param v1 Value 1
47
+ * @param v2 Value 2
48
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
49
+ */
50
+ function equals(v1: unknown, v2: unknown, strict?: number): boolean;
44
51
  /**
45
52
  * Format inital character to lower case or upper case
46
53
  * @param input Input string
@@ -111,12 +118,30 @@ export declare namespace Utils {
111
118
  */
112
119
  function objectEqual(obj1: {}, obj2: {}, ignoreFields?: string[], strict?: number): boolean;
113
120
  /**
114
- * Parse string (JSON) to specific type
121
+ * Get two object's unqiue properties
122
+ * @param obj1 Object 1
123
+ * @param obj2 Object 2
124
+ * @param ignoreFields Ignored fields
125
+ * @returns Unique properties
126
+ */
127
+ function objectKeys(obj1: {}, obj2: {}, ignoreFields?: string[]): Set<string>;
128
+ /**
129
+ * Get the new object's updated fields contrast to the previous object
130
+ * @param objNew New object
131
+ * @param objPre Previous object
132
+ * @param ignoreFields Ignored fields
133
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
134
+ * @returns Updated fields
135
+ */
136
+ function objectUpdated(objNew: {}, objPrev: {}, ignoreFields?: string[], strict?: number): string[];
137
+ /**
138
+ * Parse string (JSON) to specific type, no type conversion
139
+ * For type conversion, please use DataTypes.convert
115
140
  * @param input Input string
116
141
  * @param defaultValue Default value
117
142
  * @returns Parsed value
118
143
  */
119
- function parseString<T>(input: string | undefined | null, defaultValue: T): T;
144
+ function parseString<T, M = T | undefined>(input: string | undefined | null, defaultValue?: M): M extends T ? M : undefined;
120
145
  /**
121
146
  * Remove non letters
122
147
  * @param input Input string
package/lib/mjs/Utils.js CHANGED
@@ -40,6 +40,29 @@ export var Utils;
40
40
  }, 0);
41
41
  }
42
42
  Utils.charsToNumber = charsToNumber;
43
+ /**
44
+ * Two values equal
45
+ * @param v1 Value 1
46
+ * @param v2 Value 2
47
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
48
+ */
49
+ function equals(v1, v2, strict = 1) {
50
+ // Null and undefined case
51
+ if (v1 == null || v2 == null) {
52
+ if (strict <= 1 && v1 == v2)
53
+ return true;
54
+ return v1 === v2;
55
+ }
56
+ // For array and object
57
+ if (typeof v1 === 'object')
58
+ return JSON.stringify(v1) === JSON.stringify(v2);
59
+ // 1 and '1' case
60
+ if (strict === 0)
61
+ return v1 == v2;
62
+ // Strict equal
63
+ return v1 === v2;
64
+ }
65
+ Utils.equals = equals;
43
66
  /**
44
67
  * Format inital character to lower case or upper case
45
68
  * @param input Input string
@@ -82,7 +105,7 @@ export var Utils;
82
105
  }
83
106
  if (initValue != null) {
84
107
  const newValue = DataTypes.convert(value, initValue);
85
- if (newValue === initValue) {
108
+ if (Utils.equals(newValue, initValue)) {
86
109
  Reflect.deleteProperty(input, key);
87
110
  return;
88
111
  }
@@ -190,37 +213,66 @@ export var Utils;
190
213
  * @returns Result
191
214
  */
192
215
  function objectEqual(obj1, obj2, ignoreFields = [], strict = 1) {
193
- // Keys
194
- const keys = new Set([
195
- ...Object.keys(obj1).filter((item) => !ignoreFields.includes(item)),
196
- ...Object.keys(obj2).filter((item) => !ignoreFields.includes(item))
197
- ]);
216
+ // Unique keys
217
+ const keys = Utils.objectKeys(obj1, obj2, ignoreFields);
198
218
  for (const key of keys) {
199
219
  // Values
200
220
  const v1 = Reflect.get(obj1, key);
201
221
  const v2 = Reflect.get(obj2, key);
202
- // Null and undefined case
203
- if (v1 == null && v2 == null && strict <= 1)
204
- continue;
205
- // 1 and '1' case
206
- if (strict === 0 && v1 == v2)
207
- continue;
208
- // Strict equal
209
- if (v1 !== v2)
222
+ if (!Utils.equals(v1, v2, strict))
210
223
  return false;
211
224
  }
212
225
  return true;
213
226
  }
214
227
  Utils.objectEqual = objectEqual;
215
228
  /**
216
- * Parse string (JSON) to specific type
229
+ * Get two object's unqiue properties
230
+ * @param obj1 Object 1
231
+ * @param obj2 Object 2
232
+ * @param ignoreFields Ignored fields
233
+ * @returns Unique properties
234
+ */
235
+ function objectKeys(obj1, obj2, ignoreFields = []) {
236
+ // All keys
237
+ const allKeys = [...Object.keys(obj1), ...Object.keys(obj2)].filter((item) => !ignoreFields.includes(item));
238
+ // Unique keys
239
+ return new Set(allKeys);
240
+ }
241
+ Utils.objectKeys = objectKeys;
242
+ /**
243
+ * Get the new object's updated fields contrast to the previous object
244
+ * @param objNew New object
245
+ * @param objPre Previous object
246
+ * @param ignoreFields Ignored fields
247
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
248
+ * @returns Updated fields
249
+ */
250
+ function objectUpdated(objNew, objPrev, ignoreFields = [], strict = 1) {
251
+ // Fields
252
+ const fields = [];
253
+ // Unique keys
254
+ const keys = Utils.objectKeys(objNew, objPrev, ignoreFields);
255
+ for (const key of keys) {
256
+ // Values
257
+ const vNew = Reflect.get(objNew, key);
258
+ const vPrev = Reflect.get(objPrev, key);
259
+ if (!Utils.equals(vNew, vPrev, strict)) {
260
+ fields.push(key);
261
+ }
262
+ }
263
+ return fields;
264
+ }
265
+ Utils.objectUpdated = objectUpdated;
266
+ /**
267
+ * Parse string (JSON) to specific type, no type conversion
268
+ * For type conversion, please use DataTypes.convert
217
269
  * @param input Input string
218
270
  * @param defaultValue Default value
219
271
  * @returns Parsed value
220
272
  */
221
273
  function parseString(input, defaultValue) {
222
- // Undefined case, return default value
223
- if (input == null)
274
+ // Undefined and empty case, return default value
275
+ if (input == null || input === '')
224
276
  return defaultValue;
225
277
  // String
226
278
  if (typeof defaultValue === 'string')
@@ -238,16 +290,11 @@ export var Utils;
238
290
  // Return
239
291
  return json;
240
292
  }
241
- catch (e) {
242
- console.log('Utils.parseString error', e);
293
+ catch {
294
+ if (defaultValue == null)
295
+ return input;
243
296
  return defaultValue;
244
297
  }
245
- /*
246
- finally part will still return the boolean value
247
- finally {
248
- return defaultValue
249
- }
250
- */
251
298
  }
252
299
  Utils.parseString = parseString;
253
300
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/shared",
3
- "version": "1.0.76",
3
+ "version": "1.0.80",
4
4
  "description": "TypeScript shared utilities and functions",
5
5
  "main": "lib/cjs/index.js",
6
6
  "module": "lib/mjs/index.js",
@@ -55,13 +55,13 @@
55
55
  "dependencies": {},
56
56
  "devDependencies": {
57
57
  "@types/jest": "^27.0.3",
58
- "@typescript-eslint/eslint-plugin": "^5.4.0",
59
- "@typescript-eslint/parser": "^5.4.0",
60
- "eslint": "^8.2.0",
58
+ "@typescript-eslint/eslint-plugin": "^5.8.0",
59
+ "@typescript-eslint/parser": "^5.8.0",
60
+ "eslint": "^8.5.0",
61
61
  "eslint-config-airbnb-base": "^15.0.0",
62
62
  "eslint-plugin-import": "^2.25.3",
63
- "jest": "^27.3.1",
64
- "ts-jest": "^27.0.7",
65
- "typescript": "^4.5.2"
63
+ "jest": "^27.4.5",
64
+ "ts-jest": "^27.1.2",
65
+ "typescript": "^4.5.4"
66
66
  }
67
67
  }
@@ -49,7 +49,10 @@ export namespace StorageUtils {
49
49
  * @param key Key name
50
50
  * @param defaultValue Default value
51
51
  */
52
- export function getLocalData<T>(key: string, defaultValue: T) {
52
+ export function getLocalData<T, M = T | undefined>(
53
+ key: string,
54
+ defaultValue?: M
55
+ ): M extends T ? M : undefined {
53
56
  // Get storage
54
57
  const data = localStorage.getItem(key);
55
58
 
@@ -72,7 +75,10 @@ export namespace StorageUtils {
72
75
  * Get session storage data
73
76
  * @param key Key name
74
77
  */
75
- export function getSessionData<T>(key: string, defaultValue: T) {
78
+ export function getSessionData<T, M = T | undefined>(
79
+ key: string,
80
+ defaultValue?: M
81
+ ): M extends T ? M : undefined {
76
82
  // Get storage
77
83
  const data = sessionStorage.getItem(key);
78
84
 
package/src/Utils.ts CHANGED
@@ -93,6 +93,30 @@ export namespace Utils {
93
93
  }, 0);
94
94
  }
95
95
 
96
+ /**
97
+ * Two values equal
98
+ * @param v1 Value 1
99
+ * @param v2 Value 2
100
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
101
+ */
102
+ export function equals(v1: unknown, v2: unknown, strict = 1) {
103
+ // Null and undefined case
104
+ if (v1 == null || v2 == null) {
105
+ if (strict <= 1 && v1 == v2) return true;
106
+ return v1 === v2;
107
+ }
108
+
109
+ // For array and object
110
+ if (typeof v1 === 'object')
111
+ return JSON.stringify(v1) === JSON.stringify(v2);
112
+
113
+ // 1 and '1' case
114
+ if (strict === 0) return v1 == v2;
115
+
116
+ // Strict equal
117
+ return v1 === v2;
118
+ }
119
+
96
120
  /**
97
121
  * Format inital character to lower case or upper case
98
122
  * @param input Input string
@@ -142,7 +166,7 @@ export namespace Utils {
142
166
 
143
167
  if (initValue != null) {
144
168
  const newValue = DataTypes.convert(value, initValue);
145
- if (newValue === initValue) {
169
+ if (Utils.equals(newValue, initValue)) {
146
170
  Reflect.deleteProperty(input, key);
147
171
  return;
148
172
  }
@@ -269,42 +293,87 @@ export namespace Utils {
269
293
  ignoreFields: string[] = [],
270
294
  strict = 1
271
295
  ) {
272
- // Keys
273
- const keys = new Set([
274
- ...Object.keys(obj1).filter((item) => !ignoreFields.includes(item)),
275
- ...Object.keys(obj2).filter((item) => !ignoreFields.includes(item))
276
- ]);
296
+ // Unique keys
297
+ const keys = Utils.objectKeys(obj1, obj2, ignoreFields);
277
298
 
278
299
  for (const key of keys) {
279
300
  // Values
280
301
  const v1 = Reflect.get(obj1, key);
281
302
  const v2 = Reflect.get(obj2, key);
282
303
 
283
- // Null and undefined case
284
- if (v1 == null && v2 == null && strict <= 1) continue;
304
+ if (!Utils.equals(v1, v2, strict)) return false;
305
+ }
306
+
307
+ return true;
308
+ }
309
+
310
+ /**
311
+ * Get two object's unqiue properties
312
+ * @param obj1 Object 1
313
+ * @param obj2 Object 2
314
+ * @param ignoreFields Ignored fields
315
+ * @returns Unique properties
316
+ */
317
+ export function objectKeys(
318
+ obj1: {},
319
+ obj2: {},
320
+ ignoreFields: string[] = []
321
+ ) {
322
+ // All keys
323
+ const allKeys = [...Object.keys(obj1), ...Object.keys(obj2)].filter(
324
+ (item) => !ignoreFields.includes(item)
325
+ );
326
+
327
+ // Unique keys
328
+ return new Set(allKeys);
329
+ }
285
330
 
286
- // 1 and '1' case
287
- if (strict === 0 && v1 == v2) continue;
331
+ /**
332
+ * Get the new object's updated fields contrast to the previous object
333
+ * @param objNew New object
334
+ * @param objPre Previous object
335
+ * @param ignoreFields Ignored fields
336
+ * @param strict Strict level, 0 with ==, 1 === but null equal undefined, 2 ===
337
+ * @returns Updated fields
338
+ */
339
+ export function objectUpdated(
340
+ objNew: {},
341
+ objPrev: {},
342
+ ignoreFields: string[] = [],
343
+ strict = 1
344
+ ) {
345
+ // Fields
346
+ const fields: string[] = [];
347
+
348
+ // Unique keys
349
+ const keys = Utils.objectKeys(objNew, objPrev, ignoreFields);
288
350
 
289
- // Strict equal
290
- if (v1 !== v2) return false;
351
+ for (const key of keys) {
352
+ // Values
353
+ const vNew = Reflect.get(objNew, key);
354
+ const vPrev = Reflect.get(objPrev, key);
355
+
356
+ if (!Utils.equals(vNew, vPrev, strict)) {
357
+ fields.push(key);
358
+ }
291
359
  }
292
360
 
293
- return true;
361
+ return fields;
294
362
  }
295
363
 
296
364
  /**
297
- * Parse string (JSON) to specific type
365
+ * Parse string (JSON) to specific type, no type conversion
366
+ * For type conversion, please use DataTypes.convert
298
367
  * @param input Input string
299
368
  * @param defaultValue Default value
300
369
  * @returns Parsed value
301
370
  */
302
- export function parseString<T>(
371
+ export function parseString<T, M = T | undefined>(
303
372
  input: string | undefined | null,
304
- defaultValue: T
305
- ): T {
306
- // Undefined case, return default value
307
- if (input == null) return defaultValue;
373
+ defaultValue?: M
374
+ ): M extends T ? M : undefined {
375
+ // Undefined and empty case, return default value
376
+ if (input == null || input === '') return <any>defaultValue;
308
377
 
309
378
  // String
310
379
  if (typeof defaultValue === 'string') return <any>input;
@@ -313,7 +382,7 @@ export namespace Utils {
313
382
  // Date
314
383
  if (defaultValue instanceof Date) {
315
384
  const date = new Date(input);
316
- if (date == null) return defaultValue;
385
+ if (date == null) return <any>defaultValue;
317
386
  return <any>date;
318
387
  }
319
388
 
@@ -321,17 +390,11 @@ export namespace Utils {
321
390
  const json = JSON.parse(input);
322
391
 
323
392
  // Return
324
- return <T>json;
325
- } catch (e) {
326
- console.log('Utils.parseString error', e);
327
- return defaultValue;
328
- }
329
- /*
330
- finally part will still return the boolean value
331
- finally {
332
- return defaultValue
393
+ return json;
394
+ } catch {
395
+ if (defaultValue == null) return <any>input;
396
+ return <any>defaultValue;
333
397
  }
334
- */
335
398
  }
336
399
 
337
400
  /**