@kbn/utility-types: DotObject and DedotObject (#139539)

This commit is contained in:
Dario Gieselaar 2022-08-31 17:51:34 +02:00 committed by GitHub
parent 38fda420ac
commit c258e52147
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 165 additions and 2 deletions

2
.github/CODEOWNERS vendored
View file

@ -144,6 +144,8 @@ x-pack/examples/files_example @elastic/kibana-app-services
/src/core/types/elasticsearch @elastic/apm-ui
/packages/kbn-apm-synthtrace/ @elastic/apm-ui
/packages/kbn-shared-svg @elastic/apm-ui
/packages/kbn-utility-types/src/dot.ts @dgieselaar
/packages/kbn-utility-types/src/dot_test.ts @dgieselaar
#CC# /src/plugins/apm_oss/ @elastic/apm-ui
#CC# /x-pack/plugins/observability/ @elastic/apm-ui

View file

@ -8,6 +8,7 @@ PKG_REQUIRE_NAME = "@kbn/utility-types"
SOURCE_FILES = glob(
[
"src/serializable/**",
"src/dot.ts",
"index.ts"
],
exclude = [

View file

@ -5,7 +5,6 @@ TypeScript utility types for usage in Kibana.
- This package re-exports a subset of the items in [`utility-types`](https://github.com/piotrwitek/utility-types)
- You can also add more utility types here.
## Usage
```ts
@ -15,10 +14,11 @@ type A = Observable<string>;
type B = UnwrapObservable<A>; // string
```
## Reference
- `Assign<T, U>` &mdash; From `U` assign properties to `T` (just like object assign).
- `DotObject<T>` &mdash; Convert type `T` to a flattened structure.
- `DedotObject<T>` &mdash; The inverse of `DotObject<T>`: convert the flattened type `T` to a deeply-nested type.
- `Ensure<T, X>` &mdash; Makes sure `T` is of type `X`.
- `ObservableLike<T>` &mdash; Minimal interface for an object resembling an `Observable`.
- `PublicContract<T>` &mdash; Returns an object with public keys only.

View file

@ -136,3 +136,5 @@ export type DeepPartial<T> = T extends any[]
export interface DeepPartialArray<T> extends Array<DeepPartial<T>> {}
export type DeepPartialObject<T> = { [P in keyof T]+?: DeepPartial<T[P]> };
export type { DotObject, DedotObject } from './src/dot';

View file

@ -0,0 +1,65 @@
/*
* 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 { DeepPartial, ValuesType } from 'utility-types';
import { UnionToIntersection } from '..';
type DedotKey<
TObject extends Record<string, any>,
TKey extends keyof TObject,
TValue
> = TKey extends `${infer THead}.${infer TTail}`
? {
[key in THead]: DedotKey<TObject, TTail, TValue>;
}
: { [key in TKey]: TValue };
export type DedotObject<TObject extends Record<string, any>> = UnionToIntersection<
Exclude<
ValuesType<{
[TKey in keyof TObject]: {} extends Pick<TObject, TKey>
? DeepPartial<DedotKey<TObject, TKey, Exclude<TObject[TKey], undefined>>>
: DedotKey<TObject, TKey, TObject[TKey]>;
}>,
undefined
>
>;
type ToArray<TObject> = TObject extends Record<string, any>
? {
[TKey in keyof TObject]: Array<TObject[TKey]>;
}
: never;
type DotKey<
TObject extends Record<string, any>,
TKey extends keyof TObject & string,
TPrefix extends string
> = TObject[TKey] extends Array<infer TValueType>
? ToArray<DotObject<TValueType, `${TPrefix}${TKey}.`>>
: TObject[TKey] extends Record<string, any>
? DotObject<TObject[TKey], `${TPrefix}${TKey}.`>
: { [key in `${TPrefix}${TKey}`]: TObject[TKey] };
type _DotObject<TObject extends Record<string, any>, TPrefix extends string = ''> = ValuesType<{
[TKey in keyof TObject & string]: {} extends Pick<TObject, TKey>
? Partial<DotKey<Required<TObject>, TKey, TPrefix>>
: DotKey<TObject, TKey, TPrefix>;
}>;
export type DotObject<
TObject extends Record<string, any>,
TPrefix extends string = ''
> = UnionToIntersection<_DotObject<TObject, TPrefix>>;
export type DotKeysOf<TObject extends Record<string, any>> = keyof DotObject<TObject>;
export type PickDotted<
TObject extends Record<string, any>,
TPickKey extends DotKeysOf<TObject>
> = DedotObject<Pick<DotObject<TObject>, TPickKey>>;

View file

@ -0,0 +1,93 @@
/*
* 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 { expectAssignable, expectNotType, expectType } from 'tsd';
import { DedotObject, DotObject } from '../../dot';
interface TestA {
'my.dotted.key': string;
'my.dotted.partial.key'?: string;
'ym.dotted.partial.key'?: string;
}
interface TestB {
my: {
undotted: {
key: number;
};
partial?: {
key: string;
};
};
ym?: {
partial: {
key: string;
};
};
}
interface Dotted {
'my.undotted.key': number;
'my.partial.key'?: string;
'ym.partial.key'?: string;
}
interface Dedotted {
my: {
dotted: {
key: string;
partial?: {
key?: string;
};
};
};
ym?: {
dotted?: {
partial?: {
key?: string;
};
};
};
}
const dedotted1 = {} as DedotObject<TestA>;
const dotted1 = {} as DotObject<TestB>;
expectAssignable<DedotObject<TestA>>({} as Dedotted);
expectAssignable<DotObject<TestB>>({} as Dotted);
expectAssignable<Dedotted>({} as DedotObject<TestA>);
expectAssignable<Dotted>({} as DotObject<TestB>);
expectType<string | undefined>(dedotted1.ym?.dotted?.partial?.key?.toString());
expectType<string>(dotted1['my.undotted.key'].toString());
expectNotType<string>(dotted1['my.partial.key']);
expectType<string | undefined>(dotted1['my.partial.key']?.toString());
expectNotType<{ baz: string }>({} as DedotObject<TestA>);
expectNotType<{ baz: string }>({} as DotObject<TestB>);
expectNotType<{ my: { dotted: { key: string }; partial: { key: number } } }>(
{} as DedotObject<TestA>
);
interface ObjectWithArray {
span: {
links: Array<{
trace: {
id: string;
};
span: {
id: string;
};
}>;
};
}
expectType<DotObject<ObjectWithArray>>({
'span.links.span.id': [''],
'span.links.trace.id': [''],
});