Skip to content

Commit

Permalink
fix(TypeScript): Reduce number, and simplify, type guards
Browse files Browse the repository at this point in the history
We where exporting quite a lot of type guard function, several of which
were effectively aliases  (e.g. `isListItem`) some of which effiectively duplicated
the functionality of others.

This commit attempts to simplify the guards and reduce them
to a mimimal, but still useful set. It also aims to reduce the confusion
between a guard for a specific type `isA` and a guard for a family of types
e.g. `isTypeOf`. The previous `isCreativeWork` was actually testing for waht is now
`isTypeOf('CreativeWork')(node)` but could be confused for `isA('CreativeWork', node)`.
  • Loading branch information
nokome committed Jun 17, 2021
1 parent ab5f373 commit d348d17
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 215 deletions.
149 changes: 76 additions & 73 deletions ts/util/guards.test.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,87 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */

import { blockContentTypes, inlineContentTypes, TypeMap } from '../types'
import {
audioObject,
blockContentTypes,
code,
codeChunk,
codeTypes,
entity,
imageObject,
inlineContentTypes,
mediaObject,
mediaObjectTypes,
person,
} from '../types'
import {
isA,
isBlockContent,
isEntity,
isInlineContent,
isInlineEntity,
isPrimitive,
isType,
nodeIs,
typeIs,
isTypeOf,
} from './guards'

const primitives = [null, true, false, NaN, 2, 'string']

const typeMap = {
someType: 'someType',
myCustomType: 'myCustomType',
} as unknown as TypeMap
const primitives = [
null,
true,
false,
NaN,
2,
'string',
[],
[1, 2, 3],
{},
{ 'not-type': 'foo' },
]

describe('typeIs', () => {
it('finds the given type', () => {
// @ts-ignore
expect(typeIs(typeMap)(typeMap.myCustomType)).toBe(true)
describe('isPrimitive', () => {
test.each(primitives)('returns true for primitive value of "%s"', (node) => {
expect(isPrimitive(node)).toBe(true)
})

it('returns false when queried type is not in the type map', () => {
expect(typeIs(typeMap)('otherType')).toBe(false)
})
test('it returns false for Object with type property', () =>
expect(isPrimitive({ type: 'Emphasis' })).toBe(false))
})

describe('nodeIs', () => {
test('it returns false for undefined values', () =>
// @ts-ignore
expect(nodeIs(typeMap)(undefined)).toBe(false))
describe('isEntity', () => {
test('it returns true for any object with type property', () =>
expect(isEntity({ type: 'foo' })).toBe(true))

test.each(primitives)('returns false for primitive value of "%s"', (node) => {
expect(nodeIs(typeMap)(node)).toBe(false)
expect(isEntity(node)).toBe(false)
})
})

test('it returns false for empty Arrays', () =>
expect(nodeIs(typeMap)([])).toBe(false))
describe('isTypeOf', () => {
const isTypeOfMediaObject = isTypeOf(mediaObjectTypes)

test('it returns false for Arrays with content', () =>
expect(nodeIs(typeMap)([{ type: 'someType' }])).toBe(false))
it('returns true for nodes of the type and its descendants', () => {
expect(isTypeOfMediaObject(mediaObject({ contentUrl: '' }))).toBe(true)
expect(isTypeOfMediaObject(audioObject({ contentUrl: '' }))).toBe(true)
expect(isTypeOfMediaObject(imageObject({ contentUrl: '' }))).toBe(true)
})

test('it returns false for Objects without a "type" key', () =>
expect(nodeIs(typeMap)({ content: ['someContent'] })).toBe(false))
it('returns false for nodes that are of an ancestor type', () => {
expect(isTypeOfMediaObject(entity())).toBe(false)
})

test('it returns false for Objects containing a "type" key not found in the typeMap', () =>
expect(nodeIs(typeMap)({ type: 'someOtherType' })).toBe(false))
it('returns false for unrelated node types', () => {
expect(isTypeOfMediaObject(person())).toBe(false)
})

test('it returns true for Objects containing a "type" key found in the typeMap', () =>
// @ts-ignore
expect(nodeIs(typeMap)({ type: typeMap.someType })).toBe(true))
test.each(primitives)('returns false for primitive value of "%s"', (node) => {
expect(isEntity(node)).toBe(false)
})
})

describe('isA', () => {
const person = { type: 'Person' }
const para = { type: 'Paragraph', content: [] }

test('it returns false for undefined types', () => {
// This is a compile error too
// @ts-ignore
// @ts-expect-error
expect(isA(person, 'Foo')).toBe(false)
})

Expand All @@ -85,8 +105,7 @@ describe('isType', () => {
const para = { type: 'Paragraph', content: [] }

test('it returns false for undefined types', () => {
// This is a compile error too
// @ts-ignore
// @ts-expect-error
expect(isType('Foo')(person)).toBe(false)
})

Expand All @@ -99,42 +118,6 @@ describe('isType', () => {
})
})

describe('isPrimitive', () => {
test.each(primitives)('returns true for primitive value of "%s"', (node) => {
expect(isPrimitive(node)).toBe(true)
})

test('it returns false for empty Arrays', () =>
expect(isPrimitive([])).toBe(false))

test('it returns false for Arrays with content', () =>
expect(isPrimitive([{ type: 'someType' }])).toBe(false))

test('it returns false for Objects', () =>
expect(isPrimitive({ type: 'someOtherType' })).toBe(false))
})

describe('isInlineEntity', () => {
test.each(primitives)('returns false for primitive value of "%s"', (node) => {
expect(isInlineEntity(node)).toBe(false)
})

test('it returns false for empty Arrays', () =>
expect(isInlineEntity([])).toBe(false))

test('it returns false for Arrays with content', () =>
expect(isInlineEntity([{ type: 'someType' }])).toBe(false))

test('it returns false for Objects containing a "type" key not found in the typeMap', () =>
expect(isInlineEntity({ type: 'someOtherType' })).toBe(false))

test('it returns false for BlockContent type', () =>
expect(isInlineEntity({ type: 'Paragraph' })).toBe(false))

test('it returns true for Objects containing a "type" key found in the typeMap', () =>
expect(isInlineEntity({ type: 'CodeExpression' })).toBe(true))
})

describe('isInlineContent', () => {
test.each(primitives)('returns true for primitive value of "%s"', (type) => {
expect(isInlineContent(type)).toBe(true)
Expand All @@ -154,3 +137,23 @@ describe('isInlineContent', () => {
}
)
})

describe('isBlockContent', () => {
test.each(primitives)('returns false for primitive value of "%s"', (type) => {
expect(isBlockContent(type)).toBe(false)
})

test.each(Object.values(blockContentTypes))(
'returns true for BlockContent type of "%s"',
(type) => {
expect(isBlockContent({ type })).toBe(true)
}
)

test.each(Object.values(inlineContentTypes))(
'returns false for InlineContent type of "%s"',
(type) => {
expect(isBlockContent({ type })).toBe(false)
}
)
})
Loading

0 comments on commit d348d17

Please sign in to comment.