From fad4c911ddfd736d5929b40326c47454eac76d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20Unneb=C3=A4ck?= Date: Mon, 13 Aug 2018 12:50:35 +0100 Subject: [PATCH] Add nullable types to TypeScript typings --- index.d.ts | 54 +++++++++++++++++++++++++--------------- test/typings.ts | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 20 deletions(-) diff --git a/index.d.ts b/index.d.ts index acf79df..dd1a11f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,32 +1,25 @@ -type AnySchema = NullSchema | BooleanSchema | NumberSchema | StringSchema | AnyEnumSchema | AnyArraySchema | AnyObjectSchema | AnyAllOptionalObjectSchema | AnyOneOfSchema +type AnySchema = NullSchema | BooleanSchema | NullableBooleanSchema | NumberSchema | NullableNumberSchema | StringSchema | NullableStringSchema | AnyEnumSchema | AnyArraySchema | AnyNullableArraySchema | AnyObjectSchema | AnyNullableObjectSchema | AnyAllOptionalObjectSchema | AnyNullableAllOptionalObjectSchema | AnyOneOfSchema type StringKeys = (keyof T) & string -interface NullSchema { - type: 'null' -} +interface NullSchema { type: 'null' } -interface BooleanSchema { - type: 'boolean' -} +interface BooleanSchema { type: 'boolean' } +interface NullableBooleanSchema { type: ('boolean' | 'null')[] } -interface NumberSchema { - type: 'number' -} +interface NumberSchema { type: 'number' } +interface NullableNumberSchema { type: ('number' | 'null')[] } -interface StringSchema { - type: 'string' -} +interface StringSchema { type: 'string' } +interface NullableStringSchema { type: ('string' | 'null')[] } interface AnyEnumSchema extends EnumSchema {} -interface EnumSchema { - enum: Enum[] -} +interface EnumSchema { enum: Enum[] } interface AnyArraySchema extends ArraySchema {} -interface ArraySchema { - type: 'array' - items: ItemSchema -} +interface ArraySchema { type: 'array', items: ItemSchema } + +interface AnyNullableArraySchema extends NullableArraySchema {} +interface NullableArraySchema { type: ('array' | 'null')[], items: ItemSchema } interface AnyObjectSchema extends ObjectSchema, string> {} interface ObjectSchema, Required extends StringKeys> { @@ -36,6 +29,14 @@ interface ObjectSchema, Required ex required: Required[] } +interface AnyNullableObjectSchema extends NullableObjectSchema, string> {} +interface NullableObjectSchema, Required extends StringKeys> { + additionalProperties?: boolean + type: ('object' | 'null')[] + properties: Properties + required: Required[] +} + interface AnyAllOptionalObjectSchema extends AllOptionalObjectSchema> {} interface AllOptionalObjectSchema> { additionalProperties?: boolean @@ -43,6 +44,13 @@ interface AllOptionalObjectSchema> properties: Properties } +interface AnyNullableAllOptionalObjectSchema extends NullableAllOptionalObjectSchema> {} +interface NullableAllOptionalObjectSchema> { + additionalProperties?: boolean + type: ('object' | 'null')[] + properties: Properties +} + interface AnyOneOfSchema { oneOf: AnySchema[] } interface ArrayFromSchema extends Array> {} @@ -55,11 +63,17 @@ type TypeFromSchema = ( Schema extends EnumSchema ? Enum : Schema extends NullSchema ? null : Schema extends BooleanSchema ? boolean + : Schema extends NullableBooleanSchema ? (boolean | null) : Schema extends NumberSchema ? number + : Schema extends NullableNumberSchema ? (number | null) : Schema extends StringSchema ? string + : Schema extends NullableStringSchema ? (string | null) : Schema extends ArraySchema ? ArrayFromSchema + : Schema extends NullableArraySchema ? (ArrayFromSchema | null) : Schema extends ObjectSchema ? ObjectFromSchema + : Schema extends NullableObjectSchema ? (ObjectFromSchema | null) : Schema extends AllOptionalObjectSchema ? ObjectFromSchema + : Schema extends NullableAllOptionalObjectSchema ? (ObjectFromSchema | null) : never ) diff --git a/test/typings.ts b/test/typings.ts index 9b5d35b..b8d3f89 100644 --- a/test/typings.ts +++ b/test/typings.ts @@ -343,3 +343,68 @@ if (overengineeredColorValidator(input)) { if (input !== 'yellow' && input !== 'cream' && input !== 'red' && input !== 'pink' && input !== 'green' && input !== 'olive' && input !== 'blue') assertType<'teal'>(input) if (input !== 'cream' && input !== 'red' && input !== 'pink' && input !== 'green' && input !== 'olive' && input !== 'blue' && input !== 'teal') assertType<'yellow'>(input) } + +const nullableStringValidator = createValidator({ + type: ['string', 'null'] +}) + +if (nullableStringValidator(input)) { + if (typeof input !== 'object') assertType(input) + if (typeof input !== 'string') assertType(input) +} + +const nullableNameValidator = createValidator({ + type: 'object', + properties: { + name: { type: ['string', 'null'] } + }, + required: [ + 'name' + ] +}) + +if (nullableNameValidator(input)) { + if (typeof input.name !== 'object') assertType(input.name) + if (typeof input.name !== 'string') assertType(input.name) +} + +const nullableInventoryValidator = createValidator({ + type: 'object', + properties: { + inventory: { + type: ['array', 'null'], + items: { type: 'string' } + } + }, + required: [ + 'inventory' + ] +}) + +if (nullableInventoryValidator(input)) { + if (input.inventory === null) assertType(input.inventory) + if (input.inventory !== null) assertType(input.inventory) +} + +const nullableParentValidator = createValidator({ + type: 'object', + properties: { + parent: { + type: ['object', 'null'], + properties: { + name: { type: 'string' } + }, + required: [ + 'name' as 'name' + ] + } + }, + required: [ + 'parent' + ] +}) + +if (nullableParentValidator(input)) { + if (input.parent === null) assertType(input.parent) + if (input.parent !== null) assertType(input.parent.name) +}