mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
3509a5abb9
commit
4a3c148298
@ -515,39 +515,38 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "58"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "59"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "60"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "61"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "62"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "63"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "64"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "65"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "66"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "67"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "68"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "69"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "70"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "71"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "72"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "73"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "74"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "61"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "62"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "63"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "64"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "65"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "66"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "67"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "68"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "69"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "70"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "71"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "72"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "73"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "74"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "75"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "76"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "77"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "76"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "77"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "78"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "79"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "80"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "79"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "80"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "81"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "82"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "83"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "82"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "83"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "84"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "85"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "86"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "85"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "86"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "87"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "88"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "89"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "88"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "89"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "90"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "91"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "92"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "93"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "91"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "92"]
|
||||
],
|
||||
"packages/grafana-data/src/utils/Registry.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
@ -1847,14 +1846,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Styles should be written using objects.", "4"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "5"]
|
||||
],
|
||||
"public/app/features/alerting/unified/api/alertmanager.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/alerting/unified/api/ruler.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/AlertLabel.tsx:5381": [
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"]
|
||||
],
|
||||
@ -2968,12 +2959,11 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "7"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "8"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "9"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "10"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "9"]
|
||||
],
|
||||
"public/app/features/dashboard/components/DashNav/DashNavButton.tsx:5381": [
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"]
|
||||
@ -5405,8 +5395,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "5"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "7"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "6"]
|
||||
],
|
||||
"public/app/features/variables/state/keyedVariablesReducer.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@ -7438,8 +7427,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/types/unified-alerting-dto.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/test/core/redux/reduxTester.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
|
61
packages/grafana-data/src/types/data.test.ts
Normal file
61
packages/grafana-data/src/types/data.test.ts
Normal 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);
|
||||
});
|
||||
});
|
@ -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;
|
||||
}
|
||||
|
@ -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';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { urlUtil } from '@grafana/data';
|
||||
import { isObject, urlUtil } from '@grafana/data';
|
||||
import { getBackendSrv, isFetchError } from '@grafana/runtime';
|
||||
import {
|
||||
AlertmanagerAlert,
|
||||
@ -206,18 +206,24 @@ function receiversResponseContainsErrors(result: TestReceiversResult) {
|
||||
);
|
||||
}
|
||||
|
||||
function isTestReceiversResult(data: any): data is TestReceiversResult {
|
||||
const receivers = data?.receivers;
|
||||
|
||||
if (Array.isArray(receivers)) {
|
||||
return receivers.every(
|
||||
(receiver: any) => typeof receiver.name === 'string' && Array.isArray(receiver.grafana_managed_receiver_configs)
|
||||
);
|
||||
function isTestReceiversResult(data: unknown): data is TestReceiversResult {
|
||||
if (isObject(data) && 'receivers' in data && Array.isArray(data.receivers)) {
|
||||
return data.receivers.every(isSingleTestRecieverResult);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isSingleTestRecieverResult(receiver: unknown): receiver is TestReceiversResult {
|
||||
return (
|
||||
isObject(receiver) &&
|
||||
'name' in receiver &&
|
||||
typeof receiver.name === 'string' &&
|
||||
'grafana_managed_receiver_configs' in receiver &&
|
||||
Array.isArray(receiver.grafana_managed_receiver_configs)
|
||||
);
|
||||
}
|
||||
|
||||
function getReceiverResultError(receiversResult: TestReceiversResult) {
|
||||
return receiversResult.receivers
|
||||
.flatMap((receiver) =>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { isObject } from '@grafana/data';
|
||||
import { FetchResponse, getBackendSrv } from '@grafana/runtime';
|
||||
import { RulerDataSourceConfig } from 'app/types/unified-alerting';
|
||||
import { PostableRulerRuleGroupDTO, RulerRuleGroupDTO, RulerRulesConfigDTO } from 'app/types/unified-alerting-dto';
|
||||
@ -155,8 +156,13 @@ async function rulerGetRequest<T>(url: string, empty: T, params?: Record<string,
|
||||
}
|
||||
|
||||
function isResponseError(error: unknown): error is FetchResponse<ErrorResponseMessage> {
|
||||
const hasErrorMessage = (error as FetchResponse<ErrorResponseMessage>).data != null;
|
||||
const hasErrorCode = Number.isFinite((error as FetchResponse<ErrorResponseMessage>).status);
|
||||
if (!isObject(error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasErrorMessage = 'data' in error && error.data !== null && error.data !== undefined;
|
||||
const hasErrorCode = 'status' in error && Number.isFinite(error.status);
|
||||
|
||||
return hasErrorCode && hasErrorMessage;
|
||||
}
|
||||
|
||||
|
@ -52,8 +52,10 @@ interface PanelWithExportableLibraryPanel {
|
||||
libraryPanel: LibraryPanel;
|
||||
}
|
||||
|
||||
function isExportableLibraryPanel(p: any): p is PanelWithExportableLibraryPanel {
|
||||
return p.libraryPanel && typeof p.libraryPanel.name === 'string' && typeof p.libraryPanel.uid === 'string';
|
||||
function isExportableLibraryPanel(
|
||||
p: PanelModel | PanelWithExportableLibraryPanel
|
||||
): p is PanelWithExportableLibraryPanel {
|
||||
return Boolean(p.libraryPanel?.name && p.libraryPanel?.uid);
|
||||
}
|
||||
|
||||
interface DataSources {
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
getDataSourceRef,
|
||||
isDataSourceRef,
|
||||
isEmptyObject,
|
||||
isObject,
|
||||
LoadingState,
|
||||
TimeRange,
|
||||
TypedVariableModel,
|
||||
@ -1105,10 +1106,6 @@ export function upgradeLegacyQueries(
|
||||
};
|
||||
}
|
||||
|
||||
function isDataQueryType(query: any): query is DataQuery {
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return query.hasOwnProperty('refId') && typeof query.refId === 'string';
|
||||
function isDataQueryType(query: unknown): query is DataQuery {
|
||||
return isObject(query) && 'refId' in query && typeof query.refId === 'string';
|
||||
}
|
||||
|
@ -38,11 +38,8 @@ export function isGrafanaAlertState(state: string): state is GrafanaAlertState {
|
||||
export function isAlertStateWithReason(
|
||||
state: PromAlertingRuleState | GrafanaAlertStateWithReason
|
||||
): state is GrafanaAlertStateWithReason {
|
||||
return (
|
||||
state !== null &&
|
||||
typeof state !== 'undefined' &&
|
||||
!Object.values(PromAlertingRuleState).includes(state as PromAlertingRuleState)
|
||||
);
|
||||
const propAlertingRuleStateValues: string[] = Object.values(PromAlertingRuleState);
|
||||
return state !== null && state !== undefined && !propAlertingRuleStateValues.includes(state);
|
||||
}
|
||||
|
||||
export function mapStateWithReasonToBaseState(
|
||||
|
Loading…
Reference in New Issue
Block a user