Skip to content

Commit

Permalink
support lazy runtime types
Browse files Browse the repository at this point in the history
  • Loading branch information
jloleysens committed May 22, 2024
1 parent 89c4159 commit 843e989
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 1 deletion.
5 changes: 5 additions & 0 deletions packages/kbn-config-schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
URIType,
StreamType,
UnionTypeOptions,
Lazy,
} from './src/types';

export type { AnyType, ConditionalType, TypeOf, Props, SchemaStructureEntry, NullableProps };
Expand Down Expand Up @@ -216,6 +217,10 @@ function conditional<A extends ConditionalTypeValue, B, C>(
return new ConditionalType(leftOperand, rightOperand, equalType, notEqualType, options);
}

export function lazy<T>(id: string) {
return new Lazy<T>(id);
}

export const schema = {
any,
arrayOf,
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-config-schema/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ export { URIType } from './uri_type';
export { NeverType } from './never_type';
export type { IpOptions } from './ip_type';
export { IpType } from './ip_type';
export { Lazy } from './lazy';
71 changes: 71 additions & 0 deletions packages/kbn-config-schema/src/types/lazy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { Type, schema, lazy } from '../..';

interface RecursiveType {
name: string;
self: undefined | RecursiveType;
}

// Test our recursive type inference
{
const id = 'recursive';
// @ts-expect-error
const testObject: Type<RecursiveType> = schema.object(
{
name: schema.string(),
notSelf: lazy<RecursiveType>(id), // this declaration should fail
},
{ meta: { id } }
);
}

describe('lazy', () => {
const id = 'recursive';
const object: Type<RecursiveType> = schema.object(
{
name: schema.string(),
self: lazy<RecursiveType>(id),
},
{ meta: { id } }
);

it('allows recursive runtime types to be defined', () => {
const self: RecursiveType = {
name: 'self1',
self: {
name: 'self2',
self: {
name: 'self3',
self: {
name: 'self4',
self: undefined,
},
},
},
};
const { value, error } = object.getSchema().validate(self);
expect(error).toBeUndefined();
expect(value).toEqual(self);
});

it('detects invalid recursive types as expected', () => {
const invalidSelf = {
name: 'self1',
self: {
name: 123,
self: undefined,
},
};

const { error, value } = object.getSchema().validate(invalidSelf);
expect(value).toEqual(invalidSelf);
expect(error?.message).toBe('expected value of type [string] but got [number]');
});
});
16 changes: 16 additions & 0 deletions packages/kbn-config-schema/src/types/lazy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { Type } from './type';
import { internals } from '../internals';

export class Lazy<T> extends Type<T> {
constructor(id: string) {
super(internals.link(`#${id}`));
}
}
3 changes: 2 additions & 1 deletion packages/kbn-config-schema/src/types/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ export abstract class Type<V> {
schema = schema.description(options.meta.description);
}
if (options.meta.id) {
schema = schema.meta({ [META_FIELD_X_OAS_REF_ID]: options.meta.id });
const id = options.meta.id;
schema = schema.id(id).meta({ [META_FIELD_X_OAS_REF_ID]: id });
}
if (options.meta.deprecated) {
schema = schema.meta({ [META_FIELD_X_OAS_DEPRECATED]: true });
Expand Down

0 comments on commit 843e989

Please sign in to comment.