Kibana: config-schema incorrectly types a Maybe field as `T | undefined` instead of optional on objects

Created on 16 Apr 2020  路  5Comments  路  Source: elastic/kibana

Kibana version: 7.7+ (on master)

Describe the bug:
If you use schema.maybe() on an object , the prop is only marked as T | undefined, but it should be optional (perhaps both, rather than instead).

Screenshot 2020-04-16 at 11 17 55

Screenshot 2020-04-16 at 11 18 14

Steps to reproduce:

  1. Define an object like so:
const schemaWithOptional = schema.object({
  prop: schema.maybe(schema.string()),
});
export type SchemaWithOptional = TypeOf<typeof schemaWithOptional>;
  1. Look at typescript signature of SchemaWithOptional - the prop field is marked as string | undefined, meaning you can't omit the prop field and have to set it as undefined.
interface SchemaWithOptional {
   prop: string | undefined;
}

Expected behavior:
The type should be:

interface SchemaWithOptional {
   prop?: string;
}

or perhaps:

interface SchemaWithOptional {
   prop?: string | undefined;
}
Core bug

Most helpful comment

haha sorry if I broke you @pgayvallet

All 5 comments

Pinging @elastic/kibana-platform (Team:Platform)

I think The type should be

interface SchemaWithOptional {
   prop?: string | undefined;
}

However, technically, this requires the parent object schema's Type to be dependant on the children schema types, which I'm not sure is doable in typescript.

Will take a look.

This all goes down to

https://github.com/elastic/kibana/blob/9318862f1967d6fefdfca5e94417ff1617e5ba06/packages/kbn-config-schema/src/types/object_type.ts#L31

Would need to find a TS way to check if TypeOf<P[K]> extends MaybeType

So, after a few hours hating typescript:

This can be fixed by changing

type ObjectResultType<P extends Props> = Readonly<{ [K in keyof P]: TypeOf<P[K]> }>;

with

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]
>;

export type ObjectResultType<P extends Props> = Readonly<
  { [K in keyof OptionalProperties<P>]?: TypeOf<P[K]> } &
    { [K in keyof RequiredProperties<P>]: TypeOf<P[K]> }
>;

The TypeOf is fixed:

const type = schema.object({
    required: schema.string(),
    optional: schema.maybe(schema.string()),
  });

  type SchemaType = TypeOf<typeof type>;

  let foo: SchemaType = { // no error
    required: 'foo',
  };
  foo = { // no error
    required: 'hello',
    optional: undefined,
  };
  foo = { // no error
    required: 'hello',
    optional: 'bar',
  };

 foo = { // error
    optional: 'bar',
  };
 foo = { // error
  };

However this change causes the router validator (src/core/server/http/router/validator/validator.ts) to just implode, with

Screenshot 2020-04-17 at 00 02 23

This is directly related to this type

https://github.com/elastic/kibana/blob/d8f94b179280c6c37330bb31f8a31ccae19efd00/src/core/server/http/router/validator/validator.ts#L83-L90

However this file is a very sensible piece of TS magic, and I couldn't find a proper way to fix the typings without force casting everywhere.

If someone wants to have a look, feel free.

haha sorry if I broke you @pgayvallet

Was this page helpful?
0 / 5 - 0 ratings

Related issues

treussart picture treussart  路  3Comments

timroes picture timroes  路  3Comments

timmolter picture timmolter  路  3Comments

bradvido picture bradvido  路  3Comments

socialmineruser1 picture socialmineruser1  路  3Comments