Skip to content

Commit db63059

Browse files
authored
Merge pull request #889 from jan-molak/features/to-json
feat(object): toJSON available as a standalone function
2 parents 61ca85f + 82f2da6 commit db63059

File tree

5 files changed

+78
-70
lines changed

5 files changed

+78
-70
lines changed

.github/workflows/main.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
strategy:
1616
matrix:
17-
node-version: [ 16.x, 18.x, 20.x ]
17+
node-version: [ 16.x, 18.x, 20.x, 22.x ]
1818

1919
steps:
2020
- uses: actions/checkout@v4

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
},
3939
"homepage": "https://jan-molak.github.io/tiny-types/",
4040
"engines": {
41-
"node": "^16 || ^18 || ^20",
42-
"npm": "^8 || ^9 || ^10"
41+
"node": "^16 || ^18 || ^20 || ^22"
4342
},
4443
"devDependencies": {
4544
"@types/chai": "4.3.17",

src/TinyType.ts

Lines changed: 2 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ensure } from './ensure';
2-
import { equal, isRecord, significantFieldsOf, stringify } from './objects';
2+
import { equal, significantFieldsOf, stringify, toJSON } from './objects';
33
import { isDefined } from './predicates';
4-
import { JSONObject, JSONValue, Serialisable } from './types';
4+
import { JSONValue, Serialisable } from './types';
55

66
/**
77
* @desc The {@link TinyTypeOf} can be used to define simple
@@ -137,68 +137,3 @@ export abstract class TinyType implements Serialisable {
137137
}, {}) as JSONValue;
138138
}
139139
}
140-
141-
function toJSON(value: any): JSONValue | undefined {
142-
switch (true) {
143-
case value && !! value.toJSON:
144-
return value.toJSON();
145-
case value && Array.isArray(value):
146-
return value.map(v => {
147-
return v === undefined
148-
? null
149-
: toJSON(v) as JSONValue;
150-
});
151-
case value && value instanceof Map:
152-
return mapToJSON(value);
153-
case value && value instanceof Set:
154-
return toJSON(Array.from(value));
155-
case value && isRecord(value):
156-
return recordToJSON(value);
157-
case value && value instanceof Error:
158-
return errorToJSON(value);
159-
case isSerialisablePrimitive(value):
160-
return value;
161-
default:
162-
return JSON.stringify(value);
163-
}
164-
}
165-
166-
function mapToJSON(map: Map<any, any>): JSONObject {
167-
const serialised = Array.from(map, ([key, value]) => [ toJSON(key), toJSON(value) ]);
168-
169-
return Object.fromEntries(serialised);
170-
}
171-
172-
function recordToJSON(value: Record<any, any>): JSONObject {
173-
const serialised = Object.entries(value)
174-
.map(([ k, v ]) => [ toJSON(k), toJSON(v) ]);
175-
176-
return Object.fromEntries(serialised);
177-
}
178-
179-
function errorToJSON(value: Error): JSONObject {
180-
return Object.getOwnPropertyNames(value)
181-
.reduce((serialised, key) => {
182-
serialised[key] = toJSON(value[key])
183-
return serialised;
184-
}, { }) as JSONObject;
185-
}
186-
187-
function isSerialisableNumber(value: unknown): value is number {
188-
return typeof value === 'number'
189-
&& ! Number.isNaN(value)
190-
&& value !== Number.NEGATIVE_INFINITY
191-
&& value !== Number.POSITIVE_INFINITY;
192-
}
193-
194-
function isSerialisablePrimitive(value: unknown): value is string | boolean | number | null | undefined {
195-
if (['string', 'boolean'].includes(typeof value)) {
196-
return true;
197-
}
198-
199-
if (value === null || value === undefined) {
200-
return true;
201-
}
202-
203-
return isSerialisableNumber(value);
204-
}

src/objects/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from './isObject';
44
export * from './isRecord';
55
export * from './significantFields';
66
export * from './stringify';
7+
export * from './toJSON';

src/objects/toJSON.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* eslint-disable unicorn/filename-case */
2+
import { JSONObject, JSONValue } from '../types';
3+
import { isRecord } from './isRecord';
4+
5+
/**
6+
* Serialises the object to a JSON representation.
7+
*
8+
* @param value
9+
*/
10+
export function toJSON(value: any): JSONValue | undefined { // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
11+
switch (true) {
12+
case value && !! value.toJSON:
13+
return value.toJSON();
14+
case value && Array.isArray(value):
15+
return value.map(v => {
16+
return v === undefined
17+
? null
18+
: toJSON(v) as JSONValue;
19+
});
20+
case value && value instanceof Map:
21+
return mapToJSON(value);
22+
case value && value instanceof Set:
23+
return toJSON(Array.from(value));
24+
case value && isRecord(value):
25+
return recordToJSON(value);
26+
case value && value instanceof Error:
27+
return errorToJSON(value);
28+
case isSerialisablePrimitive(value):
29+
return value;
30+
default:
31+
return JSON.stringify(value);
32+
}
33+
}
34+
35+
function mapToJSON(map: Map<any, any>): JSONObject {
36+
const serialised = Array.from(map, ([key, value]) => [ toJSON(key), toJSON(value) ]);
37+
38+
return Object.fromEntries(serialised);
39+
}
40+
41+
function recordToJSON(value: Record<any, any>): JSONObject {
42+
const serialised = Object.entries(value)
43+
.map(([ k, v ]) => [ toJSON(k), toJSON(v) ]);
44+
45+
return Object.fromEntries(serialised);
46+
}
47+
48+
function errorToJSON(value: Error): JSONObject {
49+
return Object.getOwnPropertyNames(value)
50+
.reduce((serialised, key) => {
51+
serialised[key] = toJSON(value[key])
52+
return serialised;
53+
}, { }) as JSONObject;
54+
}
55+
56+
function isSerialisableNumber(value: unknown): value is number {
57+
return typeof value === 'number'
58+
&& ! Number.isNaN(value)
59+
&& value !== Number.NEGATIVE_INFINITY
60+
&& value !== Number.POSITIVE_INFINITY;
61+
}
62+
63+
function isSerialisablePrimitive(value: unknown): value is string | boolean | number | null | undefined {
64+
if (['string', 'boolean'].includes(typeof value)) {
65+
return true;
66+
}
67+
68+
if (value === null || value === undefined) {
69+
return true;
70+
}
71+
72+
return isSerialisableNumber(value);
73+
}

0 commit comments

Comments
 (0)