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:
Jerko Steiner 2019-09-15 11:40:30 +07:00
parent 6cbcf3c69c
commit fb41c2ce69
3 changed files with 96 additions and 0 deletions

View 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')
})
})
})

View 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'
}

View File

@ -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'