chore: Improve linting within @k8slens/core

- Turning on @typescript-eslint/recommended-requiring-type-checking
- Turning off @typescript-eslint/no-unnecessary-type-assertion (due too many false positives)
- Making @typescript-eslint/no-explicit-any an error (except in tests)

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton committed 2023-06-01 09:17:17 -04:00
1 parent 6184c2f03b
commit fa44b795d4
734 files changed
+32216 -4566

No files matched your search

@@ -3,9 +3,9 @@ export interface EventEmitterOptions {
prepend?: boolean; // put listener to the beginning
}
export type EventEmitterCallback<D extends any[]> = (...data: D) => void | boolean;
export type EventEmitterCallback<D extends unknown[]> = (...data: D) => void | boolean;
export class EventEmitter<D extends any[]> {
export class EventEmitter<D extends unknown[]> {
protected listeners: [EventEmitterCallback<D>, EventEmitterOptions][] = [];
addListener(callback: EventEmitterCallback<D>, options: EventEmitterOptions = {}) {
@@ -14,9 +14,9 @@ import type { Logger } from "@k8slens/logger";
import type Fetch from "@k8slens/node-fetch";
import type { RequestInit, Response } from "@k8slens/node-fetch";
import type { Defaulted } from "@k8slens/utilities";
import { isObject, isString, json } from "@k8slens/utilities";
import { isArray, isObject, isString, json } from "@k8slens/utilities";
export interface JsonApiData {}
export type JsonApiData = Record<string, never>;
export interface JsonApiError {
code?: number;
@@ -56,11 +56,11 @@ export interface JsonApiLog {
method: string;
reqUrl: string;
reqInit: RequestInit;
data?: any;
error?: any;
data?: unknown;
error?: unknown;
}
export type GetRequestOptions = () => Promise<RequestInit>;
export type GetRequestOptions = () => RequestInit | Promise<RequestInit>;
export interface JsonApiConfig {
apiBase: string;
@@ -236,7 +236,7 @@ export class JsonApi<Data = JsonApiData, Params extends JsonApiParams<Data> = Js
const text = await res.text();
const parseResponse = json.parse(text || "{}");
const data = parseResponse.callWasSuccessful ? (parseResponse.response as Data) : (text as Data);
const data = parseResponse.isOk ? (parseResponse.value as Data) : (text as Data);
if (status >= 200 && status < 300) {
this.onData.emit(data, res);
@@ -271,7 +271,7 @@ export class JsonApi<Data = JsonApiData, Params extends JsonApiParams<Data> = Js
const { errors, message } = error as { errors?: { title: string }[]; message?: string };
if (Array.isArray(errors)) {
if (isArray(errors)) {
return errors.map((error) => error.title);
}
@@ -2,9 +2,24 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { DiContainerForInjection } from "@ogre-tools/injectable";
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
import type { KubeApi } from "@k8slens/kube-api";
export const kubeApiInjectionToken = getInjectionToken<KubeApi<any, any>>({
export const kubeApiInjectionToken = getInjectionToken<KubeApi>({
id: "kube-api-injection-token",
});
export interface KubeApiInjectableParts<Api> {
id: string;
instantiate: (
di: DiContainerForInjection,
) => Api extends KubeApi<infer Kube, infer Data> ? KubeApi<Kube, Data> & Api : never;
}
export const getKubeApiInjectable = <Api>(parts: KubeApiInjectableParts<Api>) =>
getInjectable({
id: parts.id,
instantiate: (di) => parts.instantiate(di),
injectionToken: kubeApiInjectionToken,
});
@@ -184,7 +184,7 @@ export type KubeApiWatchCallback<T extends KubeJsonApiData = KubeJsonApiData> =
error: KubeStatus | Response | null | Record<string, unknown>,
) => void;
export interface KubeApiWatchOptions<Object extends KubeObject, Data extends KubeJsonApiDataFor<Object>> {
export interface KubeApiWatchOptions<Kube extends KubeObject, Data extends KubeJsonApiDataFor<Kube>> {
/**
* If the resource is namespaced then the default is `"default"`
*/
@@ -248,7 +248,7 @@ export type SpecificResourceDescriptor<Scope extends KubeObjectScope> = {
*/
name: string;
} & (Scope extends KubeObjectScope.Cluster
? {}
? { namespace?: undefined }
: Scope extends KubeObjectScope.Namespace
? {
/**
@@ -278,8 +278,8 @@ export interface KubeApiDependencies {
}
export class KubeApi<
Object extends KubeObject = KubeObject,
Data extends KubeJsonApiDataFor<Object> = KubeJsonApiDataFor<Object>,
Kube extends KubeObject = KubeObject,
Data extends KubeJsonApiDataFor<Kube> = KubeJsonApiDataFor<Kube>,
> {
readonly kind: string;
@@ -297,7 +297,7 @@ export class KubeApi<
readonly isNamespaced: boolean;
public readonly objectConstructor: KubeObjectConstructor<Object, Data>;
public readonly objectConstructor: KubeObjectConstructor<Kube, Data>;
protected readonly request: KubeJsonApi;
@@ -315,7 +315,7 @@ export class KubeApi<
protected readonly allowedUsableVersions: Partial<Record<string, string[]>> | undefined;
constructor(protected readonly dependencies: KubeApiDependencies, opts: KubeApiOptions<Object, Data>) {
constructor(protected readonly dependencies: KubeApiDependencies, opts: KubeApiOptions<Kube, Data>) {
const {
objectConstructor,
request = this.dependencies.maybeKubeApi,
@@ -382,13 +382,15 @@ export class KubeApi<
try {
const { apiPrefix, apiGroup, resource } = parsedApi;
const list = (await this.request.get(`${apiPrefix}/${apiGroup}`)) as KubeApiResourceVersionList;
const resourceVersions = getOrderedVersions(list, this.allowedUsableVersions?.[apiGroup]);
const list = await this.request.get(`${apiPrefix}/${apiGroup}`);
const resourceVersions = getOrderedVersions(
list as unknown as KubeApiResourceVersionList,
this.allowedUsableVersions?.[apiGroup],
);
for (const resourceVersion of resourceVersions) {
const { resources } = (await this.request.get(
`${apiPrefix}/${resourceVersion.groupVersion}`,
)) as KubeApiResourceList;
const res = await this.request.get(`${apiPrefix}/${resourceVersion.groupVersion}`);
const resources = res.resources as { name: string }[];
if (resources.some(({ name }) => name === resource)) {
return {
@@ -496,7 +498,7 @@ export class KubeApi<
return query;
}
protected parseResponse(data: unknown, namespace?: string): Object | Object[] | null {
protected parseResponse(data: unknown, namespace?: string): Kube | Kube[] | null {
if (!data) {
return null;
}
@@ -559,10 +561,7 @@ export class KubeApi<
});
}
async list(
{ namespace = "", reqInit }: KubeApiListOptions = {},
query?: KubeApiQueryParams,
): Promise<Object[] | null> {
async list({ namespace = "", reqInit }: KubeApiListOptions = {}, query?: KubeApiQueryParams): Promise<Kube[] | null> {
await this.checkPreferredVersion();
const url = this.formatUrlForListing(namespace);
@@ -580,7 +579,7 @@ export class KubeApi<
throw new Error(`GET multiple request to ${url} returned not an array: ${JSON.stringify(parsed)}`);
}
async get(desc: ResourceDescriptor, query?: KubeApiQueryParams): Promise<Object | null> {
async get(desc: ResourceDescriptor, query?: KubeApiQueryParams): Promise<Kube | null> {
await this.checkPreferredVersion();
const url = this.formatUrlForNotListing(desc);
@@ -596,8 +595,8 @@ export class KubeApi<
async create(
{ name, namespace }: Partial<ResourceDescriptor>,
partialData?: PartialDeep<Object>,
): Promise<Object | null> {
partialData?: PartialDeep<Kube>,
): Promise<Kube | null> {
await this.checkPreferredVersion();
const apiUrl = this.formatUrlForNotListing({ namespace });
@@ -619,7 +618,7 @@ export class KubeApi<
return parsed;
}
async update({ name, namespace }: ResourceDescriptor, data: PartialDeep<Object>): Promise<Object | null> {
async update({ name, namespace }: ResourceDescriptor, data: PartialDeep<Kube>): Promise<Kube | null> {
await this.checkPreferredVersion();
const apiUrl = this.formatUrlForNotListing({ namespace, name });
@@ -674,23 +673,19 @@ export class KubeApi<
return res as Scale;
}
async patch(desc: ResourceDescriptor, data: PartialDeep<Object>): Promise<Object | null>;
async patch(desc: ResourceDescriptor, data: PartialDeep<Kube>): Promise<Kube | null>;
async patch(desc: ResourceDescriptor, data: PartialDeep<Kube>, strategy: "strategic" | "merge"): Promise<Kube | null>;
async patch(desc: ResourceDescriptor, data: Patch, strategy: "json"): Promise<Kube | null>;
async patch(
desc: ResourceDescriptor,
data: PartialDeep<Object>,
strategy: "strategic" | "merge",
): Promise<Object | null>;
async patch(desc: ResourceDescriptor, data: Patch, strategy: "json"): Promise<Object | null>;
async patch(
desc: ResourceDescriptor,
data: PartialDeep<Object> | Patch,
data: PartialDeep<Kube> | Patch,
strategy: KubeApiPatchType,
): Promise<Object | null>;
): Promise<Kube | null>;
async patch(
desc: ResourceDescriptor,
data: PartialDeep<Object> | Patch,
data: PartialDeep<Kube> | Patch,
strategy: KubeApiPatchType = "strategic",
): Promise<Object | null> {
): Promise<Kube | null> {
await this.checkPreferredVersion();
const apiUrl = this.formatUrlForNotListing(desc);
@@ -740,7 +735,7 @@ export class KubeApi<
});
}
watch(opts?: KubeApiWatchOptions<Object, Data>): Disposer {
watch(opts?: KubeApiWatchOptions<Kube, Data>): Disposer {
let errorReceived = false;
let timedRetry: NodeJS.Timeout;
const {
@@ -1,4 +1,5 @@
export * from "./src/abort-controller";
export * from "./src/add-event-listener";
export * from "./src/array";
export * from "./src/backoff-caller";
export * from "./src/base64";
@@ -0,0 +1,16 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export function addEventListener<K extends keyof HTMLElementEventMap>(target: HTMLElement, type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions) {
target.addEventListener(type, listener, options);
return () => target.removeEventListener(type, listener, options);
}
export function addWindowEventListener<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions) {
window.addEventListener(type, listener, options);
return () => window.removeEventListener(type, listener, options);
}
@@ -12,6 +12,15 @@ function filled<T>(length: number, value: T): T[] {
return Array(length).fill(value);
}
/**
* A inference typed version of `Array(length).fill(value)`
* @param length The number of entries
* @param value The value of each of the indices
*/
function filledWith<T>(length: number, fn: (index: number) => T): T[] {
return Array(length).fill(undefined).map((value, index) => fn(index));
}
/**
* This function splits an array into two sub arrays on the first instance of
* element (from the left). If the array does not contain the element. The
@@ -52,4 +61,5 @@ export const array = {
bifurcate,
filled,
split,
filledWith,
};
@@ -56,7 +56,7 @@ export const backoffCaller = async <T, E>(fn: () => AsyncResult<T, E>, options?:
do {
result = await fn();
if (result.callWasSuccessful) {
if (result.isOk) {
return result;
}
@@ -3,20 +3,16 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { Result } from "./result";
import type { JsonValue } from "type-fest";
import { result, Result } from "./result";
export const json = {
parse: (input: string): Result<unknown, Error> => {
try {
return {
callWasSuccessful: true,
response: JSON.parse(input) as unknown,
}
return result.ok(JSON.parse(input) as unknown);
} catch (error) {
return {
callWasSuccessful: false,
error: error as Error,
}
return result.error(error as Error);
}
},
clone: <T>(input: JsonValue & T): T => JSON.parse(JSON.stringify(input)),
};
@@ -17,12 +17,12 @@ import type React from "react";
// <Icon onClick={prevDefault(() => console.log('stay on the page and open dialog'))}/>
// </a>
export function prevDefault<E extends React.SyntheticEvent | Event, R>(callback: (evt: E) => R): (event: E) => R {
export function prevDefault<E extends React.SyntheticEvent | Event>(callback: (evt: E) => unknown): (event: E) => void {
return function (evt: E) {
evt.preventDefault();
evt.stopPropagation();
return callback(evt);
return void callback(evt);
};
}
@@ -6,16 +6,34 @@
/**
* A helper type for async functions that return a `Result`
*/
export type AsyncResult<Response, Error = string> = Promise<Result<Response, Error>>;
export type AsyncResult<Value, Err> = Promise<Result<Value, Err>>;
/**
* Result describes the "error is just more data" pattern instead of using exceptions for
* normal execution
*/
export type Result<Response, Error = string> =
| (
Response extends void
? { callWasSuccessful: true; response?: undefined }
: { callWasSuccessful: true; response: Response }
)
| { callWasSuccessful: false; error: Error };
export type Result<Value, Err> =
| OkResult<Value>
| ErrorResult<Err>;
export type ErrorResult<Err> = { readonly isOk: false; readonly error: Err };
export type OkResult<Value> = (
Value extends void
? { readonly isOk: true; readonly value?: undefined }
: { readonly isOk: true; readonly value: Value }
);
export const result = {
ok: <Value>(value: Value) => ({
isOk: true,
value: value,
}) as OkResult<Value>,
error: <Err>(error: Err) => ({
isOk: false,
error,
}) as ErrorResult<Err>,
wrapError: <Err>(error: string, cause: ErrorResult<Err>) => ({
isOk: false,
error: new Error(error, { cause: cause.error }),
}) as ErrorResult<Error>,
}
@@ -123,10 +123,17 @@ export function isDefined<T>(val: T | undefined | null): val is T {
return val != null;
}
export function isFunction(val: unknown): val is (...args: unknown[]) => unknown {
export type ExtractOr<T, U, Fallback = never> = Extract<T, U> extends never ? Fallback : Extract<T, U>;
// @ts-expect-error
export declare function isFunction<T>(val: T): val is ExtractOr<T, Function, (...args: unknown[]) => unknown> {
return typeof val === "function";
}
export function isArray(val: unknown): val is unknown[] {
return Array.isArray(val);
}
/**
* Checks if the value in the second position is non-nullable
*/
@@ -196,14 +203,14 @@ export type ComputeOutputFormat<Format> = Format extends "string"
? Buffer
: string | Buffer;
export interface ChildProcessExecpetion<Format> extends ExecFileException {
export interface ChildProcessException<Format> extends ExecFileException {
stderr: ComputeOutputFormat<Format>;
stdout: ComputeOutputFormat<Format>;
}
const isStringOrBuffer = (val: unknown): val is string | Buffer => isString(val) || isBuffer(val);
export function isChildProcessError(error: unknown, format?: OutputFormat): error is ChildProcessExecpetion<typeof format> {
export function isChildProcessError(error: unknown, format?: OutputFormat): error is ChildProcessException<typeof format> {
if (!isExecFileException(error)) {
return false;
}