|
1 | | -import { filter, fx, groupBy, pipe, toAsync } from "../src"; |
| 1 | +import { |
| 2 | + entries, |
| 3 | + filter, |
| 4 | + fromEntries, |
| 5 | + fx, |
| 6 | + groupBy, |
| 7 | + map, |
| 8 | + pipe, |
| 9 | + reduce, |
| 10 | + toArray, |
| 11 | + toAsync, |
| 12 | +} from "../src"; |
2 | 13 | import { AsyncFunctionException } from "../src/_internal/error"; |
3 | 14 |
|
4 | 15 | type Obj = { |
@@ -95,4 +106,165 @@ describe("groupBy", function () { |
95 | 106 | expect(res).toEqual(then2); |
96 | 107 | }); |
97 | 108 | }); |
| 109 | + |
| 110 | + describe("Issue #233 - enum and union types", function () { |
| 111 | + enum Status { |
| 112 | + Todo = "TODO", |
| 113 | + InProgress = "IN_PROGRESS", |
| 114 | + Done = "DONE", |
| 115 | + } |
| 116 | + |
| 117 | + it("should work with enum types in entries + reduce pattern", function () { |
| 118 | + const tasks = [ |
| 119 | + { id: 1, status: Status.Todo, priority: 3 }, |
| 120 | + { id: 2, status: Status.InProgress, priority: 5 }, |
| 121 | + { id: 3, status: Status.Done, priority: 1 }, |
| 122 | + { id: 4, status: Status.Todo, priority: 2 }, |
| 123 | + ]; |
| 124 | + |
| 125 | + const result = pipe( |
| 126 | + tasks, |
| 127 | + groupBy((t) => t.status), |
| 128 | + entries, |
| 129 | + map( |
| 130 | + ([status, items]) => |
| 131 | + [ |
| 132 | + status, |
| 133 | + reduce((sum, item) => sum + item.priority, 0, items), |
| 134 | + ] as const, |
| 135 | + ), |
| 136 | + fromEntries, |
| 137 | + ); |
| 138 | + |
| 139 | + expect(result).toEqual({ |
| 140 | + [Status.Todo]: 5, |
| 141 | + [Status.InProgress]: 5, |
| 142 | + [Status.Done]: 1, |
| 143 | + }); |
| 144 | + }); |
| 145 | + |
| 146 | + it("should work with string union types", function () { |
| 147 | + type Color = "red" | "green" | "blue"; |
| 148 | + const items: Array<{ name: string; color: Color; value: number }> = [ |
| 149 | + { name: "apple", color: "red", value: 10 }, |
| 150 | + { name: "grass", color: "green", value: 20 }, |
| 151 | + { name: "sky", color: "blue", value: 30 }, |
| 152 | + { name: "rose", color: "red", value: 15 }, |
| 153 | + ]; |
| 154 | + |
| 155 | + const result = pipe( |
| 156 | + items, |
| 157 | + groupBy((item) => item.color), |
| 158 | + entries, |
| 159 | + map(([color, items]) => ({ |
| 160 | + color, |
| 161 | + total: reduce((sum, item) => sum + item.value, 0, items), |
| 162 | + count: items.length, |
| 163 | + })), |
| 164 | + toArray, |
| 165 | + ); |
| 166 | + |
| 167 | + expect(result).toEqual([ |
| 168 | + { color: "red", total: 25, count: 2 }, |
| 169 | + { color: "green", total: 20, count: 1 }, |
| 170 | + { color: "blue", total: 30, count: 1 }, |
| 171 | + ]); |
| 172 | + }); |
| 173 | + |
| 174 | + it("should work with string literal types", function () { |
| 175 | + const data = [ |
| 176 | + { type: "A" as const, amount: 100 }, |
| 177 | + { type: "B" as const, amount: 200 }, |
| 178 | + { type: "A" as const, amount: 150 }, |
| 179 | + ]; |
| 180 | + |
| 181 | + const result = pipe( |
| 182 | + data, |
| 183 | + groupBy((item) => item.type), |
| 184 | + entries, |
| 185 | + map(([type, items]) => ({ |
| 186 | + type, |
| 187 | + sum: reduce((acc, cur) => acc + cur.amount, 0, items), |
| 188 | + })), |
| 189 | + toArray, |
| 190 | + ); |
| 191 | + |
| 192 | + expect(result).toEqual([ |
| 193 | + { type: "A", sum: 250 }, |
| 194 | + { type: "B", sum: 200 }, |
| 195 | + ]); |
| 196 | + }); |
| 197 | + |
| 198 | + it("should handle numeric enum values", function () { |
| 199 | + enum NumericStatus { |
| 200 | + Pending = 0, |
| 201 | + Active = 1, |
| 202 | + Completed = 2, |
| 203 | + } |
| 204 | + |
| 205 | + const data = [ |
| 206 | + { id: 1, status: NumericStatus.Pending }, |
| 207 | + { id: 2, status: NumericStatus.Active }, |
| 208 | + { id: 3, status: NumericStatus.Pending }, |
| 209 | + ]; |
| 210 | + |
| 211 | + const result = groupBy((item) => item.status, data); |
| 212 | + |
| 213 | + expect(result).toEqual({ |
| 214 | + [NumericStatus.Pending]: [ |
| 215 | + { id: 1, status: NumericStatus.Pending }, |
| 216 | + { id: 3, status: NumericStatus.Pending }, |
| 217 | + ], |
| 218 | + [NumericStatus.Active]: [{ id: 2, status: NumericStatus.Active }], |
| 219 | + }); |
| 220 | + }); |
| 221 | + |
| 222 | + it("should handle const assertions", function () { |
| 223 | + const CONFIG = { |
| 224 | + TYPES: { |
| 225 | + ADMIN: "admin", |
| 226 | + USER: "user", |
| 227 | + GUEST: "guest", |
| 228 | + }, |
| 229 | + } as const; |
| 230 | + |
| 231 | + const users = [ |
| 232 | + { name: "Alice", type: CONFIG.TYPES.ADMIN }, |
| 233 | + { name: "Bob", type: CONFIG.TYPES.USER }, |
| 234 | + { name: "Charlie", type: CONFIG.TYPES.USER }, |
| 235 | + ]; |
| 236 | + |
| 237 | + const result = groupBy((user) => user.type, users); |
| 238 | + |
| 239 | + expect(result).toEqual({ |
| 240 | + [CONFIG.TYPES.ADMIN]: [{ name: "Alice", type: CONFIG.TYPES.ADMIN }], |
| 241 | + [CONFIG.TYPES.USER]: [ |
| 242 | + { name: "Bob", type: CONFIG.TYPES.USER }, |
| 243 | + { name: "Charlie", type: CONFIG.TYPES.USER }, |
| 244 | + ], |
| 245 | + }); |
| 246 | + }); |
| 247 | + |
| 248 | + it("should work with async callback and union types", async function () { |
| 249 | + type Priority = "low" | "medium" | "high"; |
| 250 | + const items: Array<{ name: string; priority: Priority }> = [ |
| 251 | + { name: "task1", priority: "low" }, |
| 252 | + { name: "task2", priority: "high" }, |
| 253 | + { name: "task3", priority: "low" }, |
| 254 | + ]; |
| 255 | + |
| 256 | + const result = await pipe( |
| 257 | + toAsync(items), |
| 258 | + groupBy(async (item) => item.priority), |
| 259 | + ); |
| 260 | + |
| 261 | + expect(result).toEqual({ |
| 262 | + low: [ |
| 263 | + { name: "task1", priority: "low" }, |
| 264 | + { name: "task3", priority: "low" }, |
| 265 | + ], |
| 266 | + high: [{ name: "task2", priority: "high" }], |
| 267 | + }); |
| 268 | + }); |
| 269 | + }); |
98 | 270 | }); |
0 commit comments