Skip to content

Commit

Permalink
Merge pull request #167 from mafintosh/ts-required
Browse files Browse the repository at this point in the history
Make "required" optional in TypeScript typings
  • Loading branch information
LinusU authored Aug 10, 2018
2 parents 20b299d + 88dad89 commit 7160756
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 6 deletions.
10 changes: 9 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
type AnySchema = NullSchema | BooleanSchema | NumberSchema | StringSchema | AnyEnumSchema | AnyArraySchema | AnyObjectSchema
type AnySchema = NullSchema | BooleanSchema | NumberSchema | StringSchema | AnyEnumSchema | AnyArraySchema | AnyObjectSchema | AnyAllOptionalObjectSchema
type StringKeys<T> = (keyof T) & string

interface NullSchema {
Expand Down Expand Up @@ -36,6 +36,13 @@ interface ObjectSchema<Properties extends Record<string, AnySchema>, Required ex
required: Required[]
}

interface AnyAllOptionalObjectSchema extends AllOptionalObjectSchema<Record<string, AnySchema>> {}
interface AllOptionalObjectSchema<Properties extends Record<string, AnySchema>> {
additionalProperties?: boolean
type: 'object'
properties: Properties
}

interface ArrayFromSchema<ItemSchema extends AnySchema> extends Array<TypeFromSchema<ItemSchema>> {}

type ObjectFromSchema<Properties extends Record<string, AnySchema>, Required extends StringKeys<Properties>> = {
Expand All @@ -50,6 +57,7 @@ type TypeFromSchema<Schema extends AnySchema> = (
: Schema extends StringSchema ? string
: Schema extends ArraySchema<infer ItemSchema> ? ArrayFromSchema<ItemSchema>
: Schema extends ObjectSchema<infer Properties, infer Required> ? ObjectFromSchema<Properties, Required>
: Schema extends AllOptionalObjectSchema<infer Properties> ? ObjectFromSchema<Properties, never>
: never
)

Expand Down
30 changes: 25 additions & 5 deletions test/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ const personValidator = createValidator({

if (personValidator(input)) {
assertType<string>(input.name)
assertType<number | undefined>(input.age)
input.age === undefined
input.age === 1
if (typeof input.age !== 'undefined') assertType<number>(input.age)
if (typeof input.age !== 'number') assertType<undefined>(input.age)
}

const namesValidator = createValidator({
Expand Down Expand Up @@ -139,7 +138,8 @@ const user2Validator = createValidator({

if (user2Validator(input)) {
assertType<{ first: string | undefined, last: string }>(input.name)
assertType<string | undefined>(input.name.first)
if (typeof input.name.first !== 'undefined') assertType<string>(input.name.first)
if (typeof input.name.first !== 'string') assertType<undefined>(input.name.first)
assertType<string>(input.name.last)

if (input.items !== undefined) {
Expand All @@ -165,7 +165,9 @@ const specificValuesValidator = createValidator({
})

if (specificValuesValidator(input)) {
assertType<true | 1000 | 'XX'>(input)
if (input !== true && input !== 1000) assertType<'XX'>(input)
if (input !== 1000 && input !== 'XX') assertType<true>(input)
if (input !== 'XX' && input !== true) assertType<1000>(input)
}

const metricValidator = createValidator({
Expand All @@ -184,3 +186,21 @@ if (metricValidator(input)) {
assertType<'page-view'>(input.name)
assertType<string>(input.page)
}

const noRequiredFieldsValidator = createValidator({
type: 'object',
properties: {
a: { type: 'string' },
b: { type: 'string' },
c: { type: 'string' }
}
})

if (noRequiredFieldsValidator(input)) {
if (typeof input.a !== 'string') assertType<undefined>(input.a)
if (typeof input.b !== 'string') assertType<undefined>(input.b)
if (typeof input.c !== 'string') assertType<undefined>(input.c)
if (typeof input.a !== 'undefined') assertType<string>(input.a)
if (typeof input.b !== 'undefined') assertType<string>(input.b)
if (typeof input.c !== 'undefined') assertType<string>(input.c)
}

0 comments on commit 7160756

Please sign in to comment.