Skip to content

Commit fad4c91

Browse files
committedAug 13, 2018
Add nullable types to TypeScript typings
1 parent 484197f commit fad4c91

File tree

2 files changed

+99
-20
lines changed

2 files changed

+99
-20
lines changed
 

‎index.d.ts

+34-20
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
1-
type AnySchema = NullSchema | BooleanSchema | NumberSchema | StringSchema | AnyEnumSchema | AnyArraySchema | AnyObjectSchema | AnyAllOptionalObjectSchema | AnyOneOfSchema
1+
type AnySchema = NullSchema | BooleanSchema | NullableBooleanSchema | NumberSchema | NullableNumberSchema | StringSchema | NullableStringSchema | AnyEnumSchema | AnyArraySchema | AnyNullableArraySchema | AnyObjectSchema | AnyNullableObjectSchema | AnyAllOptionalObjectSchema | AnyNullableAllOptionalObjectSchema | AnyOneOfSchema
22
type StringKeys<T> = (keyof T) & string
33

4-
interface NullSchema {
5-
type: 'null'
6-
}
4+
interface NullSchema { type: 'null' }
75

8-
interface BooleanSchema {
9-
type: 'boolean'
10-
}
6+
interface BooleanSchema { type: 'boolean' }
7+
interface NullableBooleanSchema { type: ('boolean' | 'null')[] }
118

12-
interface NumberSchema {
13-
type: 'number'
14-
}
9+
interface NumberSchema { type: 'number' }
10+
interface NullableNumberSchema { type: ('number' | 'null')[] }
1511

16-
interface StringSchema {
17-
type: 'string'
18-
}
12+
interface StringSchema { type: 'string' }
13+
interface NullableStringSchema { type: ('string' | 'null')[] }
1914

2015
interface AnyEnumSchema extends EnumSchema<any> {}
21-
interface EnumSchema<Enum> {
22-
enum: Enum[]
23-
}
16+
interface EnumSchema<Enum> { enum: Enum[] }
2417

2518
interface AnyArraySchema extends ArraySchema<AnySchema> {}
26-
interface ArraySchema<ItemSchema extends AnySchema> {
27-
type: 'array'
28-
items: ItemSchema
29-
}
19+
interface ArraySchema<ItemSchema extends AnySchema> { type: 'array', items: ItemSchema }
20+
21+
interface AnyNullableArraySchema extends NullableArraySchema<AnySchema> {}
22+
interface NullableArraySchema<ItemSchema extends AnySchema> { type: ('array' | 'null')[], items: ItemSchema }
3023

3124
interface AnyObjectSchema extends ObjectSchema<Record<string, AnySchema>, string> {}
3225
interface ObjectSchema<Properties extends Record<string, AnySchema>, Required extends StringKeys<Properties>> {
@@ -36,13 +29,28 @@ interface ObjectSchema<Properties extends Record<string, AnySchema>, Required ex
3629
required: Required[]
3730
}
3831

32+
interface AnyNullableObjectSchema extends NullableObjectSchema<Record<string, AnySchema>, string> {}
33+
interface NullableObjectSchema<Properties extends Record<string, AnySchema>, Required extends StringKeys<Properties>> {
34+
additionalProperties?: boolean
35+
type: ('object' | 'null')[]
36+
properties: Properties
37+
required: Required[]
38+
}
39+
3940
interface AnyAllOptionalObjectSchema extends AllOptionalObjectSchema<Record<string, AnySchema>> {}
4041
interface AllOptionalObjectSchema<Properties extends Record<string, AnySchema>> {
4142
additionalProperties?: boolean
4243
type: 'object'
4344
properties: Properties
4445
}
4546

47+
interface AnyNullableAllOptionalObjectSchema extends NullableAllOptionalObjectSchema<Record<string, AnySchema>> {}
48+
interface NullableAllOptionalObjectSchema<Properties extends Record<string, AnySchema>> {
49+
additionalProperties?: boolean
50+
type: ('object' | 'null')[]
51+
properties: Properties
52+
}
53+
4654
interface AnyOneOfSchema { oneOf: AnySchema[] }
4755

4856
interface ArrayFromSchema<ItemSchema extends AnySchema> extends Array<TypeFromSchema<ItemSchema>> {}
@@ -55,11 +63,17 @@ type TypeFromSchema<Schema extends AnySchema> = (
5563
Schema extends EnumSchema<infer Enum> ? Enum
5664
: Schema extends NullSchema ? null
5765
: Schema extends BooleanSchema ? boolean
66+
: Schema extends NullableBooleanSchema ? (boolean | null)
5867
: Schema extends NumberSchema ? number
68+
: Schema extends NullableNumberSchema ? (number | null)
5969
: Schema extends StringSchema ? string
70+
: Schema extends NullableStringSchema ? (string | null)
6071
: Schema extends ArraySchema<infer ItemSchema> ? ArrayFromSchema<ItemSchema>
72+
: Schema extends NullableArraySchema<infer ItemSchema> ? (ArrayFromSchema<ItemSchema> | null)
6173
: Schema extends ObjectSchema<infer Properties, infer Required> ? ObjectFromSchema<Properties, Required>
74+
: Schema extends NullableObjectSchema<infer Properties, infer Required> ? (ObjectFromSchema<Properties, Required> | null)
6275
: Schema extends AllOptionalObjectSchema<infer Properties> ? ObjectFromSchema<Properties, never>
76+
: Schema extends NullableAllOptionalObjectSchema<infer Properties> ? (ObjectFromSchema<Properties, never> | null)
6377
: never
6478
)
6579

‎test/typings.ts

+65
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,68 @@ if (overengineeredColorValidator(input)) {
343343
if (input !== 'yellow' && input !== 'cream' && input !== 'red' && input !== 'pink' && input !== 'green' && input !== 'olive' && input !== 'blue') assertType<'teal'>(input)
344344
if (input !== 'cream' && input !== 'red' && input !== 'pink' && input !== 'green' && input !== 'olive' && input !== 'blue' && input !== 'teal') assertType<'yellow'>(input)
345345
}
346+
347+
const nullableStringValidator = createValidator({
348+
type: ['string', 'null']
349+
})
350+
351+
if (nullableStringValidator(input)) {
352+
if (typeof input !== 'object') assertType<string>(input)
353+
if (typeof input !== 'string') assertType<null>(input)
354+
}
355+
356+
const nullableNameValidator = createValidator({
357+
type: 'object',
358+
properties: {
359+
name: { type: ['string', 'null'] }
360+
},
361+
required: [
362+
'name'
363+
]
364+
})
365+
366+
if (nullableNameValidator(input)) {
367+
if (typeof input.name !== 'object') assertType<string>(input.name)
368+
if (typeof input.name !== 'string') assertType<null>(input.name)
369+
}
370+
371+
const nullableInventoryValidator = createValidator({
372+
type: 'object',
373+
properties: {
374+
inventory: {
375+
type: ['array', 'null'],
376+
items: { type: 'string' }
377+
}
378+
},
379+
required: [
380+
'inventory'
381+
]
382+
})
383+
384+
if (nullableInventoryValidator(input)) {
385+
if (input.inventory === null) assertType<null>(input.inventory)
386+
if (input.inventory !== null) assertType<string[]>(input.inventory)
387+
}
388+
389+
const nullableParentValidator = createValidator({
390+
type: 'object',
391+
properties: {
392+
parent: {
393+
type: ['object', 'null'],
394+
properties: {
395+
name: { type: 'string' }
396+
},
397+
required: [
398+
'name' as 'name'
399+
]
400+
}
401+
},
402+
required: [
403+
'parent'
404+
]
405+
})
406+
407+
if (nullableParentValidator(input)) {
408+
if (input.parent === null) assertType<null>(input.parent)
409+
if (input.parent !== null) assertType<string>(input.parent.name)
410+
}

0 commit comments

Comments
 (0)
Please sign in to comment.