Chore: Stricter typing in type guards (#77809)

* Stricter typing in type guards

* create utility isObject fn

* better isAlertStateWithReason

* better name

* restore "data !== undefined"
This commit is contained in:
Josh Hunt
2023-11-13 16:11:54 +00:00
committed by GitHub
parent 3509a5abb9
commit 4a3c148298
9 changed files with 151 additions and 69 deletions

View File

@@ -0,0 +1,61 @@
import { isObject, isTruthy } from './data';
describe('isObject', () => {
it.each([
// [value, expected]
// These are objects
[{}, true],
[[], true],
[{ a: 1 }, true],
[new Date(), true],
[new Error(), true],
// These are not!
[parseInt('blabla', 10), false], // NaN
[null, false],
[undefined, false],
[-Infinity, false],
[-42, false],
[0, false],
[-0, false],
[42, false],
[Infinity, false],
['foo', false],
[true, false],
[Symbol(), false],
[() => {}, false],
])('should return %p for %p', (input, expected) => {
expect(isObject(input)).toBe(expected);
});
});
describe('isTruthy', () => {
it.each([
// [value, expected]
// These are truthy
[true, true],
[-Infinity, true],
[-42, true],
[42, true],
[Infinity, true],
['foo', true],
[{}, true],
[[], true],
[() => {}, true],
[Symbol(), true],
[new Date(), true],
// These are falsy
[false, false],
[0, false],
[-0, false],
['', false],
[null, false],
[undefined, false],
[parseInt('blabla', 10), false], // NaN
])('should return %p for %p', (input, expected) => {
expect(isTruthy(input)).toBe(expected);
});
});

View File

@@ -213,3 +213,27 @@ export interface DataConfigSource {
type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T;
export const isTruthy = <T>(value: T): value is Truthy<T> => Boolean(value);
/**
* Serves no runtime purpose - only used to make typescript check a value has been correctly
* narrowed to an object
*/
function identityObject(value: object): object {
return value;
}
/**
* Utility type predicate to check if a value is typeof object, but excludes "null".
*
* We normally discourage the use of type predicates in favor of just inline typescript narrowing,
* but this is a special case to handle null annoyingly being typeof object
*/
export function isObject(value: unknown): value is object {
if (typeof value === 'object' && value !== null) {
identityObject(value);
return true;
}
return false;
}

View File

@@ -16,6 +16,7 @@ import {
StandardEditorContext,
} from '../field';
import { PanelOptionsSupplier } from '../panel/PanelPlugin';
import { isObject } from '../types';
import { OptionsEditorItem, OptionsUIRegistryBuilder } from '../types/OptionsUIRegistryBuilder';
import { FieldConfigEditorProps, FieldConfigPropertyItem, FieldConfigEditorConfig } from '../types/fieldOverrides';
import { PanelOptionsEditorConfig, PanelOptionsEditorItem } from '../types/panel';
@@ -204,8 +205,8 @@ export class NestedPanelOptionsBuilder<TSub = any> implements OptionsEditorItem<
};
}
export function isNestedPanelOptions(item: any): item is NestedPanelOptionsBuilder {
return item.id === 'nested-panel-options';
export function isNestedPanelOptions(item: unknown): item is NestedPanelOptionsBuilder {
return isObject(item) && 'id' in item && item.id === 'nested-panel-options';
}
/**