An educational pure functional programming library in TypeScript
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add hasProperties guard and use library guards in docs and examples

+26 -15
+2 -5
docs-site/src/content/docs/examples/http-client.md
··· 264 264 retry, 265 265 pipe, 266 266 match, 267 + hasProperties, 267 268 runPromise, 268 269 runPromiseExit, 269 270 } from "../../src/index" ··· 305 306 306 307 // Type guard - validates the shape at runtime without casting 307 308 const isUser = (data: unknown): data is User => 308 - typeof data === "object" && 309 - data !== null && 310 - "id" in data && 311 - "name" in data && 312 - "email" in data 309 + hasProperties("id", "name", "email")(data) 313 310 314 311 // ============================================================================= 315 312 // SECTION 2: Fetch as an Effect
+2 -1
docs-site/src/content/docs/stories/forest-election/01-the-ballot-box-problem.mdx
··· 156 156 apValidation, 157 157 matchValidation, 158 158 match, 159 + isDefined, 159 160 pipe, 160 161 } from "purus-ts"; 161 162 ``` ··· 196 197 : pipe( 197 198 VALID_CANDIDATES.find((c) => c === candidate.trim()), 198 199 (found) => 199 - found !== undefined 200 + isDefined(found) 200 201 ? valid(found) 201 202 : invalidOne({ _tag: "InvalidCandidate", name: candidate }), 202 203 );
+2 -4
docs-site/src/content/docs/tutorial/07-the-effect-system.md
··· 267 267 268 268 ```typescript 269 269 import { 270 - type Eff, succeed, fail, pipe, flatMap, mapEff, catchAll, fromPromise 270 + type Eff, succeed, fail, pipe, flatMap, mapEff, catchAll, fromPromise, isObject, hasProperties 271 271 } from "purus-ts" 272 272 273 273 type ApiError = ··· 277 277 278 278 // Type guard for ApiError 279 279 const isApiError = (e: unknown): e is ApiError => 280 - e !== null && 281 - typeof e === "object" && 282 - "_tag" in e && 280 + hasProperties("_tag")(e) && 283 281 (e._tag === "NetworkError" || e._tag === "NotFound" || e._tag === "ServerError") 284 282 285 283 const fetchJson = <T>(url: string): Eff<T, ApiError, unknown> =>
+2 -5
examples/http-client/with-purus.ts
··· 35 35 retry, 36 36 pipe, 37 37 match, 38 + hasProperties, 38 39 runPromise, 39 40 runPromiseExit, 40 41 } from "../../src/index" ··· 76 77 77 78 // Type guard - validates the shape at runtime without casting 78 79 const isUser = (data: unknown): data is User => 79 - typeof data === "object" && 80 - data !== null && 81 - "id" in data && 82 - "name" in data && 83 - "email" in data 80 + hasProperties("id", "name", "email")(data) 84 81 85 82 // ============================================================================= 86 83 // SECTION 2: Fetch as an Effect
+6
src/data/guards.ts
··· 93 93 <K extends string>(key: K) => 94 94 <T>(x: T): x is T & Record<K, unknown> => 95 95 isObject(x) && key in x 96 + 97 + /** Creates a guard that checks if an object has all specified properties */ 98 + export const hasProperties = 99 + <K extends string>(...keys: K[]) => 100 + <T>(x: T): x is T & Record<K, unknown> => 101 + isObject(x) && keys.every((k) => k in x)
+12
tests/guards.test.ts
··· 1 1 import { describe, expect, it } from "bun:test" 2 2 import { 3 3 and, 4 + hasProperties, 4 5 isBoolean, 5 6 isDefined, 6 7 isNotNull, ··· 57 58 const xs: (number | undefined)[] = [1, undefined, 3, undefined, 5] 58 59 const result: number[] = xs.filter(isDefined) 59 60 expect(result).toEqual([1, 3, 5]) 61 + }) 62 + }) 63 + 64 + describe("property guards", () => { 65 + it("hasProperties checks for all specified keys", () => { 66 + const hasIdAndName = hasProperties("id", "name") 67 + expect(hasIdAndName({ id: 1, name: "Alice" })).toBe(true) 68 + expect(hasIdAndName({ id: 1 })).toBe(false) 69 + expect(hasIdAndName({})).toBe(false) 70 + expect(hasIdAndName(null)).toBe(false) 71 + expect(hasIdAndName("string")).toBe(false) 60 72 }) 61 73 }) 62 74