support unknown keys for object type in @kbn/schema-config (#39448)

* support unknown keys for object type in @kbn/schema-config

* add test for children objects

* update snapshot test

* allowUnknowns as TypeOptions
This commit is contained in:
Mikhail Shustov 2019-06-24 16:22:36 +02:00 committed by GitHub
parent 4d88aaa274
commit 99c36d8fa6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 10 deletions

View file

@ -39,6 +39,7 @@ import {
NumberOptions,
NumberType,
ObjectType,
ObjectTypeOptions,
Props,
RecordOfOptions,
RecordOfType,
@ -94,10 +95,7 @@ function maybe<V>(type: Type<V>): Type<V | undefined> {
return new MaybeType(type);
}
function object<P extends Props>(
props: P,
options?: TypeOptions<{ [K in keyof P]: TypeOf<P[K]> }>
): ObjectType<P> {
function object<P extends Props>(props: P, options?: ObjectTypeOptions<P>): ObjectType<P> {
return new ObjectType(props, options);
}

View file

@ -1,9 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`allowUnknowns = true affects only own keys 1`] = `"[foo.baz]: definition for this key is missing"`;
exports[`called with wrong type 1`] = `"expected a plain object value, but found [string] instead."`;
exports[`called with wrong type 2`] = `"expected a plain object value, but found [number] instead."`;
exports[`does not allow unknown keys when allowUnknowns = false 1`] = `"[bar]: definition for this key is missing"`;
exports[`fails if key does not exist in schema 1`] = `"[bar]: definition for this key is missing"`;
exports[`fails if missing required value 1`] = `"[name]: expected value of type [string] but got [undefined]"`;

View file

@ -28,7 +28,7 @@ export { LiteralType } from './literal_type';
export { MaybeType } from './maybe_type';
export { MapOfOptions, MapOfType } from './map_type';
export { NumberOptions, NumberType } from './number_type';
export { ObjectType, Props, TypeOf } from './object_type';
export { ObjectType, ObjectTypeOptions, Props, TypeOf } from './object_type';
export { RecordOfOptions, RecordOfType } from './record_type';
export { StringOptions, StringType } from './string_type';
export { UnionType } from './union_type';

View file

@ -211,3 +211,47 @@ test('individual keys can validated', () => {
`"bar is not a valid part of this schema"`
);
});
test('allow unknown keys when allowUnknowns = true', () => {
const type = schema.object(
{ foo: schema.string({ defaultValue: 'test' }) },
{ allowUnknowns: true }
);
expect(
type.validate({
bar: 'baz',
})
).toEqual({
foo: 'test',
bar: 'baz',
});
});
test('allowUnknowns = true affects only own keys', () => {
const type = schema.object(
{ foo: schema.object({ bar: schema.string() }) },
{ allowUnknowns: true }
);
expect(() =>
type.validate({
foo: {
bar: 'bar',
baz: 'baz',
},
})
).toThrowErrorMatchingSnapshot();
});
test('does not allow unknown keys when allowUnknowns = false', () => {
const type = schema.object(
{ foo: schema.string({ defaultValue: 'test' }) },
{ allowUnknowns: false }
);
expect(() =>
type.validate({
bar: 'baz',
})
).toThrowErrorMatchingSnapshot();
});

View file

@ -28,25 +28,31 @@ export type TypeOf<RT extends Type<any>> = RT['type'];
// Because of https://github.com/Microsoft/TypeScript/issues/14041
// this might not have perfect _rendering_ output, but it will be typed.
export type ObjectResultType<P extends Props> = Readonly<{ [K in keyof P]: TypeOf<P[K]> }>;
export type ObjectTypeOptions<P extends Props = any> = TypeOptions<
{ [K in keyof P]: TypeOf<P[K]> }
> & {
allowUnknowns?: boolean;
};
export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>> {
private props: Record<string, AnySchema>;
constructor(props: P, options: TypeOptions<{ [K in keyof P]: TypeOf<P[K]> }> = {}) {
constructor(props: P, options: ObjectTypeOptions<P> = {}) {
const schemaKeys = {} as Record<string, AnySchema>;
for (const [key, value] of Object.entries(props)) {
schemaKeys[key] = value.getSchema();
}
const { allowUnknowns, ...typeOptions } = options;
const schema = internals
.object()
.keys(schemaKeys)
.optional()
.default();
.default()
.unknown(Boolean(allowUnknowns));
super(schema, options);
super(schema, typeOptions);
this.props = schemaKeys;
}