Skip to content

Commit

Permalink
Change type and behaviour for undefined default #878
Browse files Browse the repository at this point in the history
  • Loading branch information
fabian-hiller committed Oct 29, 2024
1 parent b2911b3 commit 7a3f29a
Show file tree
Hide file tree
Showing 49 changed files with 424 additions and 547 deletions.
1 change: 1 addition & 0 deletions library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ All notable changes to the library will be documented in this file.
- Add `args` and `returns` action to transform functions (issue #243)
- Change types and implementation to support Standard Schema
- Change behaviour of `minValue` and `maxValue` for `NaN` (pull request #843)
- Change type and behaviour of `nullable`, `nullableAsync`, `nullish`, `nullishAsync`, `optional`, `optionalAsync`, `undefinedable` and `undefinedableAsync` for undefined default value (issue #878)
- Refactor `bytes`, `maxBytes`, `minBytes` and `notBytes` action

## v0.42.1 (September 20, 2024)
Expand Down
4 changes: 2 additions & 2 deletions library/src/methods/partial/partial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ type PartialEntries<
> = {
[TKey in keyof TEntries]: TKeys extends readonly (keyof TEntries)[]
? TKey extends TKeys[number]
? OptionalSchema<TEntries[TKey], never>
? OptionalSchema<TEntries[TKey], undefined>
: TEntries[TKey]
: OptionalSchema<TEntries[TKey], never>;
: OptionalSchema<TEntries[TKey], undefined>;
};

/**
Expand Down
4 changes: 2 additions & 2 deletions library/src/methods/partial/partialAsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ type PartialEntries<
> = {
[TKey in keyof TEntries]: TKeys extends readonly (keyof TEntries)[]
? TKey extends TKeys[number]
? OptionalSchemaAsync<TEntries[TKey], never>
? OptionalSchemaAsync<TEntries[TKey], undefined>
: TEntries[TKey]
: OptionalSchemaAsync<TEntries[TKey], never>;
: OptionalSchemaAsync<TEntries[TKey], undefined>;
};

/**
Expand Down
6 changes: 3 additions & 3 deletions library/src/schemas/looseObject/looseObject.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ describe('looseObject', () => {
{
key1: StringSchema<undefined>;
key2: OptionalSchema<StringSchema<undefined>, 'foo'>;
key3: NullishSchema<StringSchema<undefined>, never>;
key4: ObjectSchema<{ key: NumberSchema<undefined> }, never>;
key3: NullishSchema<StringSchema<undefined>, undefined>;
key4: ObjectSchema<{ key: NumberSchema<undefined> }, undefined>;
key5: SchemaWithPipe<[StringSchema<undefined>, ReadonlyAction<string>]>;
key6: UndefinedableSchema<StringSchema<undefined>, 'bar'>;
key7: SchemaWithPipe<
[
OptionalSchema<StringSchema<undefined>, never>,
OptionalSchema<StringSchema<undefined>, undefined>,
TransformAction<undefined | string, string>,
]
>;
Expand Down
6 changes: 3 additions & 3 deletions library/src/schemas/looseObject/looseObjectAsync.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ describe('looseObjectAsync', () => {
{
key1: StringSchema<undefined>;
key2: OptionalSchema<StringSchema<undefined>, 'foo'>;
key3: NullishSchema<StringSchema<undefined>, never>;
key4: ObjectSchema<{ key: NumberSchema<undefined> }, never>;
key3: NullishSchema<StringSchema<undefined>, undefined>;
key4: ObjectSchema<{ key: NumberSchema<undefined> }, undefined>;
key5: SchemaWithPipe<[StringSchema<undefined>, ReadonlyAction<string>]>;
key6: UndefinedableSchema<StringSchema<undefined>, 'bar'>;
key7: SchemaWithPipe<
[
OptionalSchema<StringSchema<undefined>, never>,
OptionalSchema<StringSchema<undefined>, undefined>,
TransformAction<undefined | string, string>,
]
>;
Expand Down
6 changes: 3 additions & 3 deletions library/src/schemas/nonNullable/nonNullable.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('nonNullable', () => {
describe('should return schema object', () => {
test('with undefined message', () => {
type Schema = NonNullableSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
undefined
>;
expectTypeOf(nonNullable(nullish(string()))).toEqualTypeOf<Schema>();
Expand All @@ -31,7 +31,7 @@ describe('nonNullable', () => {
test('with string message', () => {
expectTypeOf(nonNullable(nullish(string()), 'message')).toEqualTypeOf<
NonNullableSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
'message'
>
>();
Expand All @@ -42,7 +42,7 @@ describe('nonNullable', () => {
nonNullable(nullish(string()), () => 'message')
).toEqualTypeOf<
NonNullableSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
() => string
>
>();
Expand Down
6 changes: 3 additions & 3 deletions library/src/schemas/nonNullable/nonNullableAsync.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('nonNullableAsync', () => {
describe('should return schema object', () => {
test('with undefined message', () => {
type Schema = NonNullableSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
undefined
>;
expectTypeOf(
Expand All @@ -38,7 +38,7 @@ describe('nonNullableAsync', () => {
nonNullableAsync(nullishAsync(string()), 'message')
).toEqualTypeOf<
NonNullableSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
'message'
>
>();
Expand All @@ -49,7 +49,7 @@ describe('nonNullableAsync', () => {
nonNullableAsync(nullishAsync(string()), () => 'message')
).toEqualTypeOf<
NonNullableSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
() => string
>
>();
Expand Down
8 changes: 4 additions & 4 deletions library/src/schemas/nonNullish/nonNullish.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('nonNullish', () => {
describe('should return schema object', () => {
test('with undefined message', () => {
type Schema = NonNullishSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
undefined
>;
expectTypeOf(nonNullish(nullish(string()))).toEqualTypeOf<Schema>();
Expand All @@ -28,7 +28,7 @@ describe('nonNullish', () => {
test('with string message', () => {
expectTypeOf(nonNullish(nullish(string()), 'message')).toEqualTypeOf<
NonNullishSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
'message'
>
>();
Expand All @@ -39,7 +39,7 @@ describe('nonNullish', () => {
nonNullish(nullish(string()), () => 'message')
).toEqualTypeOf<
NonNullishSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
() => string
>
>();
Expand All @@ -48,7 +48,7 @@ describe('nonNullish', () => {

describe('should infer correct types', () => {
type Schema = NonNullishSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
undefined
>;

Expand Down
8 changes: 4 additions & 4 deletions library/src/schemas/nonNullish/nonNullishAsync.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('nonNullishAsync', () => {
describe('should return schema object', () => {
test('with undefined message', () => {
type Schema = NonNullishSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
undefined
>;
expectTypeOf(
Expand All @@ -35,7 +35,7 @@ describe('nonNullishAsync', () => {
nonNullishAsync(nullishAsync(string()), 'message')
).toEqualTypeOf<
NonNullishSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
'message'
>
>();
Expand All @@ -46,7 +46,7 @@ describe('nonNullishAsync', () => {
nonNullishAsync(nullishAsync(string()), () => 'message')
).toEqualTypeOf<
NonNullishSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
() => string
>
>();
Expand All @@ -55,7 +55,7 @@ describe('nonNullishAsync', () => {

describe('should infer correct types', () => {
type Schema = NonNullishSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
undefined
>;

Expand Down
8 changes: 4 additions & 4 deletions library/src/schemas/nonOptional/nonOptional.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe('nonOptional', () => {
describe('should return schema object', () => {
test('with undefined message', () => {
type Schema = NonOptionalSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
undefined
>;
expectTypeOf(nonOptional(nullish(string()))).toEqualTypeOf<Schema>();
Expand All @@ -28,7 +28,7 @@ describe('nonOptional', () => {
test('with string message', () => {
expectTypeOf(nonOptional(nullish(string()), 'message')).toEqualTypeOf<
NonOptionalSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
'message'
>
>();
Expand All @@ -39,7 +39,7 @@ describe('nonOptional', () => {
nonOptional(nullish(string()), () => 'message')
).toEqualTypeOf<
NonOptionalSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
() => string
>
>();
Expand All @@ -48,7 +48,7 @@ describe('nonOptional', () => {

describe('should infer correct types', () => {
type Schema = NonOptionalSchema<
NullishSchema<StringSchema<undefined>, never>,
NullishSchema<StringSchema<undefined>, undefined>,
undefined
>;

Expand Down
8 changes: 4 additions & 4 deletions library/src/schemas/nonOptional/nonOptionalAsync.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('nonOptionalAsync', () => {
describe('should return schema object', () => {
test('with undefined message', () => {
type Schema = NonOptionalSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
undefined
>;
expectTypeOf(
Expand All @@ -35,7 +35,7 @@ describe('nonOptionalAsync', () => {
nonOptionalAsync(nullishAsync(string()), 'message')
).toEqualTypeOf<
NonOptionalSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
'message'
>
>();
Expand All @@ -46,7 +46,7 @@ describe('nonOptionalAsync', () => {
nonOptionalAsync(nullishAsync(string()), () => 'message')
).toEqualTypeOf<
NonOptionalSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
() => string
>
>();
Expand All @@ -55,7 +55,7 @@ describe('nonOptionalAsync', () => {

describe('should infer correct types', () => {
type Schema = NonOptionalSchemaAsync<
NullishSchemaAsync<StringSchema<undefined>, never>,
NullishSchemaAsync<StringSchema<undefined>, undefined>,
undefined
>;

Expand Down
10 changes: 5 additions & 5 deletions library/src/schemas/nullable/nullable.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { nullable, type NullableSchema } from './nullable.ts';

describe('nullable', () => {
describe('should return schema object', () => {
test('with never default', () => {
expectTypeOf(nullable(string())).toEqualTypeOf<
NullableSchema<StringSchema<undefined>, never>
>();
test('with undefined default', () => {
type Schema = NullableSchema<StringSchema<undefined>, undefined>;
expectTypeOf(nullable(string())).toEqualTypeOf<Schema>();
expectTypeOf(nullable(string(), undefined)).toEqualTypeOf<Schema>();
});

test('with null default', () => {
Expand Down Expand Up @@ -41,7 +41,7 @@ describe('nullable', () => {
});

describe('should infer correct types', () => {
type Schema1 = NullableSchema<StringSchema<undefined>, never>;
type Schema1 = NullableSchema<StringSchema<undefined>, undefined>;
type Schema2 = NullableSchema<StringSchema<undefined>, null>;
type Schema3 = NullableSchema<StringSchema<undefined>, 'foo'>;
type Schema4 = NullableSchema<StringSchema<undefined>, () => null>;
Expand Down
16 changes: 12 additions & 4 deletions library/src/schemas/nullable/nullable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ describe('nullable', () => {
'~validate': expect.any(Function),
};

test('with never default', () => {
expect(nullable(string())).toStrictEqual(baseSchema);
test('with undefined default', () => {
const expected: NullableSchema<StringSchema<undefined>, undefined> = {
...baseSchema,
default: undefined,
};
expect(nullable(string())).toStrictEqual(expected);
expect(nullable(string(), undefined)).toStrictEqual(expected);
});

test('with null default', () => {
Expand Down Expand Up @@ -71,10 +76,13 @@ describe('nullable', () => {
});

describe('should return dataset without default', () => {
const schema = nullable(string(), 'foo');
test('for undefined default', () => {
expectNoSchemaIssue(nullable(string()), [null, 'foo']);
expectNoSchemaIssue(nullable(string(), undefined), [null, 'foo']);
});

test('for wrapper type', () => {
expectNoSchemaIssue(schema, ['', 'bar', '#$%']);
expectNoSchemaIssue(nullable(string(), 'foo'), ['', 'bar', '#$%']);
});
});

Expand Down
23 changes: 5 additions & 18 deletions library/src/schemas/nullable/nullable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export interface NullableSchema<
*/
export function nullable<
const TWrapped extends BaseSchema<unknown, unknown, BaseIssue<unknown>>,
>(wrapped: TWrapped): NullableSchema<TWrapped, never>;
>(wrapped: TWrapped): NullableSchema<TWrapped, undefined>;

/**
* Creates a nullable schema.
Expand All @@ -69,27 +69,23 @@ export function nullable<

export function nullable(
wrapped: BaseSchema<unknown, unknown, BaseIssue<unknown>>,
...args: unknown[]
default_?: unknown
): NullableSchema<BaseSchema<unknown, unknown, BaseIssue<unknown>>, unknown> {
// Create schema object
// @ts-expect-error
const schema: NullableSchema<
BaseSchema<unknown, unknown, BaseIssue<unknown>>,
unknown
> = {
return {
kind: 'schema',
type: 'nullable',
reference: nullable,
expects: `(${wrapped.expects} | null)`,
async: false,
wrapped,
default: default_,
'~standard': 1,
'~vendor': 'valibot',
'~validate'(dataset, config = getGlobalConfig()) {
// If value is `null`, override it with default or return dataset
if (dataset.value === null) {
// If default is specified, override value of dataset
if ('default' in this) {
if (this.default !== undefined) {
dataset.value = getDefault(this, dataset, config);
}

Expand All @@ -106,13 +102,4 @@ export function nullable(
return this.wrapped['~validate'](dataset, config);
},
};

// Add default if specified
if (0 in args) {
// @ts-expect-error
schema.default = args[0];
}

// Return schema object
return schema;
}
10 changes: 5 additions & 5 deletions library/src/schemas/nullable/nullableAsync.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { nullableAsync, type NullableSchemaAsync } from './nullableAsync.ts';

describe('nullableAsync', () => {
describe('should return schema object', () => {
test('with never default', () => {
expectTypeOf(nullableAsync(string())).toEqualTypeOf<
NullableSchemaAsync<StringSchema<undefined>, never>
>();
test('with undefined default', () => {
type Schema = NullableSchemaAsync<StringSchema<undefined>, undefined>;
expectTypeOf(nullableAsync(string())).toEqualTypeOf<Schema>();
expectTypeOf(nullableAsync(string(), undefined)).toEqualTypeOf<Schema>();
});

test('with null default', () => {
Expand Down Expand Up @@ -53,7 +53,7 @@ describe('nullableAsync', () => {
});

describe('should infer correct types', () => {
type Schema1 = NullableSchemaAsync<StringSchema<undefined>, never>;
type Schema1 = NullableSchemaAsync<StringSchema<undefined>, undefined>;
type Schema2 = NullableSchemaAsync<StringSchema<undefined>, null>;
type Schema3 = NullableSchemaAsync<StringSchema<undefined>, 'foo'>;
type Schema4 = NullableSchemaAsync<StringSchema<undefined>, () => null>;
Expand Down
Loading

0 comments on commit 7a3f29a

Please sign in to comment.