Skip to content

Commit

Permalink
feat: add t.opaque
Browse files Browse the repository at this point in the history
  • Loading branch information
jedwards1211 committed May 31, 2022
1 parent 4407abd commit 3bda3ec
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 1 deletion.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The validation errors are detailed. Adapted from the brilliant work in `flow-run
- [`t.readonlyArray(t.number())`](#treadonlyarraytnumber)
- [`t.object(properties)`](#tobjectproperties)
- [`t.object({ required?, optional?, exact? })`](#tobject-required-optional-exact-)
- [`t.opaque<DateString>(() => t.string())`](#topaquedatestring--tstring)
- [`t.readonly(objectType)`](#treadonlyobjecttype)
- [`t.merge(...objectTypes)`](#tmergeobjecttypes)
- [`t.mergeInexact(...objectTypes)`](#tmergeinexactobjecttypes)
Expand Down Expand Up @@ -386,9 +387,13 @@ PersonType.assert({ name: 1 }) // error
PersonType.assert({ name: 'dude', age: 'old' }) // error
```

### `t.opaque<DateString>(() => t.string())`

A validator that requires the value to be a string, but presents the type as `DateString` (for instance with `export opaque type DateString = string`)

### `t.readonly(objectType)`

Use `t.readOnly(t.object(...))` or `t.readOnly(t.merge(...))` etc. Doesn't require the object to be frozen, just allows the extracted type to be readonly.
Use `t.readonly(t.object(...))` or `t.readonly(t.merge(...))` etc. Doesn't require the object to be frozen, just allows the extracted type to be readonly.

### `t.merge(...objectTypes)`

Expand Down
3 changes: 3 additions & 0 deletions src/index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import NumberType from './types/NumberType'
import NumericLiteralType from './types/NumericLiteralType'
import ObjectType from './types/ObjectType'
import ObjectTypeProperty from './types/ObjectTypeProperty'
import OpaqueType from './types/OpaqueType'
import oneOf from './oneOf'
import PrimitiveLiteralType from './types/PrimitiveLiteralType'
import RecordType from './types/RecordType'
Expand Down Expand Up @@ -47,6 +48,7 @@ export {
NumericLiteralType,
ObjectType,
ObjectTypeProperty,
OpaqueType,
oneOf,
PrimitiveLiteralType,
RecordType,
Expand All @@ -68,6 +70,7 @@ export {

declare export function any(): Type<any>
declare export function unknown(): Type<mixed>
declare export function opaque<T>(type: () => Type<any>): OpaqueType<T>

declare export function array<T>(elementType: Type<T>): Type<T[]>
declare export function readonlyArray<T>(
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import NumberType from './types/NumberType'
import NumericLiteralType from './types/NumericLiteralType'
import ObjectType from './types/ObjectType'
import ObjectTypeProperty from './types/ObjectTypeProperty'
import OpaqueType from './types/OpaqueType'
import oneOf from './oneOf'
import PrimitiveLiteralType from './types/PrimitiveLiteralType'
import RecordType from './types/RecordType'
Expand Down Expand Up @@ -45,6 +46,7 @@ export {
NumericLiteralType,
ObjectType,
ObjectTypeProperty,
OpaqueType,
oneOf,
PrimitiveLiteralType,
RecordType,
Expand All @@ -66,6 +68,8 @@ export {

export const any = (): Type<any> => new AnyType()
export const unknown = (): Type<unknown> => new UnknownType()
export const opaque = <T>(type: () => Type<any>): OpaqueType<T> =>
new OpaqueType<T>(type)

export const array = <T>(elementType: Type<T>): Type<T[]> =>
new ArrayType(elementType)
Expand Down
11 changes: 11 additions & 0 deletions src/types/OpaqueType.js.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @flow

import Type from './Type'

declare class OpaqueType<T> extends Type<T> {
type: () => Type<any>;

constructor(type: () => Type<any>): void;
}

export default OpaqueType
37 changes: 37 additions & 0 deletions src/types/OpaqueType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Type from './Type'
import Validation, { IdentifierPath } from '../Validation'
import RuntimeTypeErrorItem from '../errorReporting/RuntimeTypeErrorItem'

export default class TypeReference<T> extends Type<T> {
typeName = 'TypeReference'
readonly type: () => Type<any>

constructor(type: () => Type<any>) {
super()
this.type = type
}

resolveType(): Type<any> {
return this.type().resolveType()
}

*errors(
validation: Validation,
path: IdentifierPath,
input: any
): Iterable<RuntimeTypeErrorItem> {
yield* this.type().errors(validation, path, input)
}

accepts(input: any): input is T {
return this.type().accepts(input)
}

get acceptsSomeCompositeTypes(): boolean {
return this.type().acceptsSomeCompositeTypes
}

toString(): string {
return this.type().toString()
}
}
11 changes: 11 additions & 0 deletions test/opaque.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as t from '../src'
import { expect } from 'chai'

describe(`t.opaque`, function() {
const DateString: t.OpaqueType<any> = t.opaque(() => t.string())

it(`works`, function() {
DateString.assert('foo')
expect(DateString.accepts('foo')).to.be.true
})
})

0 comments on commit 3bda3ec

Please sign in to comment.