diff --git a/package-lock.json b/package-lock.json index 29f2d10..1df672b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14907,10 +14907,10 @@ }, "packages/logger": { "name": "@shiftcode/logger", - "version": "3.0.1", + "version": "3.0.2-pr62.0", "license": "UNLICENSED", "devDependencies": { - "@shiftcode/utilities": "^4.1.0" + "@shiftcode/utilities": "^4.2.0-pr62.0" }, "engines": { "node": "^20.0.0 || ^22.0.0" @@ -14957,7 +14957,7 @@ }, "packages/utilities": { "name": "@shiftcode/utilities", - "version": "4.1.0", + "version": "4.2.0-pr62.0", "license": "MIT", "engines": { "node": "^20.0.0 || ^22.0.0" diff --git a/packages/logger/package.json b/packages/logger/package.json index 7ce09fd..ffeecb5 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -1,6 +1,6 @@ { "name": "@shiftcode/logger", - "version": "3.0.1", + "version": "3.0.2-pr62.0", "description": "logger for local and aws lambda execution", "repository": "https://github.com/shiftcode/sc-commons-public", "license": "UNLICENSED", @@ -37,7 +37,7 @@ "test:watch": "npm run test -- --watch" }, "devDependencies": { - "@shiftcode/utilities": "^4.1.0" + "@shiftcode/utilities": "^4.2.0-pr62.0" }, "peerDependencies": { "@shiftcode/utilities": "^4.0.0 || ^4.0.0-pr53" diff --git a/packages/utilities/package.json b/packages/utilities/package.json index c66fae8..acf9908 100644 --- a/packages/utilities/package.json +++ b/packages/utilities/package.json @@ -1,6 +1,6 @@ { "name": "@shiftcode/utilities", - "version": "4.1.0", + "version": "4.2.0-pr62.0", "description": "Contains some utilities", "repository": "https://github.com/shiftcode/sc-commons-public", "license": "MIT", diff --git a/packages/utilities/src/index.ts b/packages/utilities/src/index.ts index 0748aee..10ba7f4 100644 --- a/packages/utilities/src/index.ts +++ b/packages/utilities/src/index.ts @@ -7,8 +7,10 @@ export * from './lib/group-by/group-by.js' export * from './lib/http/http-constants.js' export * from './lib/json-stringify-replacer/json-stringify-replacer.function.js' export * from './lib/map-values-deep/map-values-deep.js' +export * from './lib/object/get-value-assert-defined.function.js' export * from './lib/object/omit-props.function.js' export * from './lib/object/pick-props.function.js' +export * from './lib/object/pick-props-assert-defined.function.js' export * from './lib/promise/make-deferred.function.js' export * from './lib/math/clamp.function.js' export * from './lib/math/random-int.js' diff --git a/packages/utilities/src/lib/object/get-value-assert-defined.function.spec.ts b/packages/utilities/src/lib/object/get-value-assert-defined.function.spec.ts new file mode 100644 index 0000000..2874d96 --- /dev/null +++ b/packages/utilities/src/lib/object/get-value-assert-defined.function.spec.ts @@ -0,0 +1,50 @@ +import { getValueAssertDefined } from './get-value-assert-defined.function.js' + +describe('getValueAssertDefined', () => { + interface Test { + x: string | null + y?: boolean + z: number | undefined + } + + describe('throws when not defined', () => { + const empty: Test = { x: null, z: undefined } + + test('when null', () => { + expect(() => getValueAssertDefined(empty, 'x')).toThrow() + }) + + test('when undefined', () => { + expect(() => getValueAssertDefined(empty, 'z')).toThrow() + }) + + test('when optional', () => { + expect(() => getValueAssertDefined(empty, 'y')).toThrow() + }) + }) + + describe('returns value when defined', () => { + const obj: Test = { + x: '', + y: false, + z: 0, + } + + test('when empty string', () => { + expect(getValueAssertDefined(obj, 'x')).toEqual('') + }) + test('when false', () => { + expect(getValueAssertDefined(obj, 'y')).toEqual(false) + }) + test('when zero', () => { + expect(getValueAssertDefined(obj, 'z')).toEqual(0) + }) + + test('makes it type safe', () => { + const tDefined: { num: number | null } = { num: 42 } + // assign to variable to ensure type safety + const res: number = getValueAssertDefined(tDefined, 'num') + expect(res).toEqual(42) + }) + }) +}) diff --git a/packages/utilities/src/lib/object/get-value-assert-defined.function.ts b/packages/utilities/src/lib/object/get-value-assert-defined.function.ts new file mode 100644 index 0000000..905c2e5 --- /dev/null +++ b/packages/utilities/src/lib/object/get-value-assert-defined.function.ts @@ -0,0 +1,14 @@ +import { isDefined } from '../ts-guards/is-defined.js' + +/** + * returns the value of the provided key on given object. throws if the value is null or undefined + */ +export function getValueAssertDefined(obj: T, key: K): NonNullable { + type X = NonNullable + const value: X | null | undefined = obj[key] + + if (!isDefined(value)) { + throw new Error(`Expected property "${String(key)}" to be defined. Was "${value}" instead`) + } + return value +} diff --git a/packages/utilities/src/lib/object/pick-props-assert-defined.function.spec.ts b/packages/utilities/src/lib/object/pick-props-assert-defined.function.spec.ts new file mode 100644 index 0000000..1caa104 --- /dev/null +++ b/packages/utilities/src/lib/object/pick-props-assert-defined.function.spec.ts @@ -0,0 +1,12 @@ +import { pickPropsAssertDefined } from './pick-props-assert-defined.function.js' + +describe('pickPropsAssertDefined', () => { + test('returns object with picked props', () => { + const obj = { a: true, b: 'foo', c: 42 } + expect(pickPropsAssertDefined(obj, ['a', 'c'])).toEqual({ a: true, c: 42 }) + }) + test('throws when picked prop values are null or undefined', () => { + const obj = { a: 'ok', b: null } + expect(() => pickPropsAssertDefined(obj, ['a', 'b'])).toThrow(Error) + }) +}) diff --git a/packages/utilities/src/lib/object/pick-props-assert-defined.function.ts b/packages/utilities/src/lib/object/pick-props-assert-defined.function.ts new file mode 100644 index 0000000..b92e752 --- /dev/null +++ b/packages/utilities/src/lib/object/pick-props-assert-defined.function.ts @@ -0,0 +1,21 @@ +import { isDefined } from '../ts-guards/is-defined.js' + +export type PickedPropsDefined = { + [key in K]-?: NonNullable +} + +/** + * returns an object containing the provided props with their respective value. throws if their value is null or undefined + */ +export function pickPropsAssertDefined( + obj: T, + props: readonly TProp[], +): PickedPropsDefined { + const entries = props.map((p) => { + if (!isDefined(obj[p])) { + throw new Error(`Expected property "${String(p)}" to be defined. Was "${String(obj[p])}" instead`) + } + return [p, obj[p]] + }) + return Object.fromEntries(entries) +}