mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
@kbn/utility-types: DotObject and DedotObject (#139539)
This commit is contained in:
parent
38fda420ac
commit
c258e52147
6 changed files with 165 additions and 2 deletions
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -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
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ PKG_REQUIRE_NAME = "@kbn/utility-types"
|
|||
SOURCE_FILES = glob(
|
||||
[
|
||||
"src/serializable/**",
|
||||
"src/dot.ts",
|
||||
"index.ts"
|
||||
],
|
||||
exclude = [
|
||||
|
|
|
@ -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>` — From `U` assign properties to `T` (just like object assign).
|
||||
- `DotObject<T>` — Convert type `T` to a flattened structure.
|
||||
- `DedotObject<T>` — The inverse of `DotObject<T>`: convert the flattened type `T` to a deeply-nested type.
|
||||
- `Ensure<T, X>` — Makes sure `T` is of type `X`.
|
||||
- `ObservableLike<T>` — Minimal interface for an object resembling an `Observable`.
|
||||
- `PublicContract<T>` — Returns an object with public keys only.
|
||||
|
|
|
@ -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';
|
||||
|
|
65
packages/kbn-utility-types/src/dot.ts
Normal file
65
packages/kbn-utility-types/src/dot.ts
Normal 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>>;
|
93
packages/kbn-utility-types/src/tsd_tests/test_d/dot.ts
Normal file
93
packages/kbn-utility-types/src/tsd_tests/test_d/dot.ts
Normal 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': [''],
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue