kbn/config-schema: Consider maybe properties as optional keys in ObjectType (#63838) (#64036)

* consider optional properties as optional keys in ObjectType

* fix type on security config

* fix ObjectTypeOptions
This commit is contained in:
Pierre Gayvallet 2020-04-21 11:45:30 +02:00 committed by GitHub
parent 41ff32a8e1
commit 653eb4f4d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 9 deletions

View file

@ -18,6 +18,7 @@
*/
import { schema } from '..';
import { TypeOf } from './object_type';
test('returns value by default', () => {
const type = schema.object({
@ -350,3 +351,26 @@ test('unknowns = `ignore` affects only own keys', () => {
})
).toThrowErrorMatchingInlineSnapshot(`"[foo.baz]: definition for this key is missing"`);
});
test('handles optional properties', () => {
const type = schema.object({
required: schema.string(),
optional: schema.maybe(schema.string()),
});
type SchemaType = TypeOf<typeof type>;
let foo: SchemaType = {
required: 'foo',
};
foo = {
required: 'hello',
optional: undefined,
};
foo = {
required: 'hello',
optional: 'bar',
};
expect(foo).toBeDefined();
});

View file

@ -26,9 +26,26 @@ export type Props = Record<string, Type<any>>;
export type TypeOf<RT extends Type<any>> = RT['type'];
type OptionalProperties<Base extends Props> = Pick<
Base,
{
[Key in keyof Base]: undefined extends TypeOf<Base[Key]> ? Key : never;
}[keyof Base]
>;
type RequiredProperties<Base extends Props> = Pick<
Base,
{
[Key in keyof Base]: undefined extends TypeOf<Base[Key]> ? never : Key;
}[keyof Base]
>;
// 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 ObjectResultType<P extends Props> = Readonly<
{ [K in keyof OptionalProperties<P>]?: TypeOf<P[K]> } &
{ [K in keyof RequiredProperties<P>]: TypeOf<P[K]> }
>;
interface UnknownOptions {
/**
@ -40,9 +57,7 @@ interface UnknownOptions {
unknowns?: 'allow' | 'ignore' | 'forbid';
}
export type ObjectTypeOptions<P extends Props = any> = TypeOptions<
{ [K in keyof P]: TypeOf<P[K]> }
> &
export type ObjectTypeOptions<P extends Props = any> = TypeOptions<ObjectResultType<P>> &
UnknownOptions;
export class ObjectType<P extends Props = any> extends Type<ObjectResultType<P>> {

View file

@ -17,7 +17,14 @@
* under the License.
*/
import { ValidationError, Type, schema, ObjectType, isConfigSchema } from '@kbn/config-schema';
import {
ValidationError,
Type,
schema,
ObjectType,
TypeOf,
isConfigSchema,
} from '@kbn/config-schema';
import { Stream } from 'stream';
import { RouteValidationError } from './validator_error';
@ -85,7 +92,7 @@ type RouteValidationResultType<T extends RouteValidationSpec<any> | undefined> =
T extends RouteValidationFunction<any>
? ReturnType<T>['value']
: T extends Type<any>
? ReturnType<T['validate']>
? TypeOf<T>
: undefined
>;
@ -170,7 +177,7 @@ export class RouteValidator<P = {}, Q = {}, B = {}> {
* @internal
*/
public getParams(data: unknown, namespace?: string): Readonly<P> {
return this.validate(this.config.params, this.options.unsafe?.params, data, namespace);
return this.validate(this.config.params, this.options.unsafe?.params, data, namespace) as P;
}
/**
@ -178,7 +185,7 @@ export class RouteValidator<P = {}, Q = {}, B = {}> {
* @internal
*/
public getQuery(data: unknown, namespace?: string): Readonly<Q> {
return this.validate(this.config.query, this.options.unsafe?.query, data, namespace);
return this.validate(this.config.query, this.options.unsafe?.query, data, namespace) as Q;
}
/**
@ -186,7 +193,7 @@ export class RouteValidator<P = {}, Q = {}, B = {}> {
* @internal
*/
public getBody(data: unknown, namespace?: string): Readonly<B> {
return this.validate(this.config.body, this.options.unsafe?.body, data, namespace);
return this.validate(this.config.body, this.options.unsafe?.body, data, namespace) as B;
}
/**