Add @rondo.dev/common guard fn
This is to help guard against types that can be undefined since the
recommended eslint rules recommend against using `!` operator on values
like:
const obj: Obj | undefined = ...
obj!.value
Now we can use:
const obj: Obj | undefined = ...
const obj2: Obj = guard(isDefined, obj)
obj.value
The guard function will throw an error if obj parameter is undefined.
This commit is contained in:
parent
6cbcf3c69c
commit
fb41c2ce69
60
packages/common/src/guard.test.ts
Normal file
60
packages/common/src/guard.test.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import {guard, isNumber, isString, isDefined, isUndefined, isNull} from './guard'
|
||||
|
||||
function getNumber(n: number | null): number | null {
|
||||
return n
|
||||
}
|
||||
|
||||
function getString(str: string | undefined | null): string | undefined | null {
|
||||
return str
|
||||
}
|
||||
|
||||
describe('guard', () => {
|
||||
|
||||
describe('converts T | undefined | null to T', () => {
|
||||
describe('isDefined', () => {
|
||||
it('converts T | undefined | null to T', () => {
|
||||
const s1: string | undefined | null = getString('test')
|
||||
const s2: string = guard<string>(isDefined, s1)
|
||||
expect(s2).toBe('test')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isString', () => {
|
||||
it('converts string | undefined | null to string', () => {
|
||||
const s1: string | undefined | null = getString('test')
|
||||
const s2: string = guard(isString, s1)
|
||||
expect(s2).toBe('test')
|
||||
})
|
||||
})
|
||||
|
||||
describe('isNumber', () => {
|
||||
it('converts number | undefined | null to number', () => {
|
||||
const n1: number | undefined | null = getNumber(1)
|
||||
const n2: number = guard(isNumber, n1)
|
||||
expect(n2).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isUndefined', () => {
|
||||
const s1: string | undefined | null = getString(undefined)
|
||||
const s2: undefined = guard(isUndefined, s1)
|
||||
expect(s2).toBe(undefined)
|
||||
})
|
||||
|
||||
describe('isNull', () => {
|
||||
const s1: string | undefined | null = getString(null)
|
||||
const s2: null = guard(isNull, s1)
|
||||
expect(s2).toBe(null)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('errors', () => {
|
||||
it('guards against unexpected types', () => {
|
||||
const s1: string | undefined | null = getString(null)
|
||||
expect(() => guard(isString, s1))
|
||||
.toThrowError('Value null does not satisfy constraint: isString')
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
35
packages/common/src/guard.ts
Normal file
35
packages/common/src/guard.ts
Normal file
@ -0,0 +1,35 @@
|
||||
type CheckType<T> = (t: unknown) => t is T
|
||||
|
||||
export function guard<Expected>(
|
||||
checkType: CheckType<Expected>,
|
||||
t: unknown,
|
||||
): Expected {
|
||||
if (!checkType(t)) {
|
||||
throw new Error(
|
||||
`Value ${t} does not satisfy constraint: ${checkType.name}`)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
export function isUndefined(t: unknown): t is undefined {
|
||||
return t === undefined
|
||||
}
|
||||
|
||||
export function isNull(t: unknown): t is null {
|
||||
return t === null
|
||||
}
|
||||
|
||||
export function isDefined<T>(
|
||||
t: unknown,
|
||||
): t is T {
|
||||
return t !== undefined && t !== null
|
||||
}
|
||||
|
||||
export function isNumber(t: unknown): t is number {
|
||||
return typeof t === 'number'
|
||||
}
|
||||
|
||||
export function isString(t: unknown): t is string {
|
||||
return typeof t === 'string'
|
||||
}
|
||||
@ -6,6 +6,7 @@ export * from './ILogger'
|
||||
export * from './IRole'
|
||||
export * from './StringUtils'
|
||||
export * from './filterProps'
|
||||
export * from './guard'
|
||||
export * from './indexBy'
|
||||
export * from './types'
|
||||
export * from './without'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user