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:
parent
4d88aaa274
commit
99c36d8fa6
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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]"`;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue