diff --git a/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-node-selection.ts b/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-node-selection.ts index 179018ceacd42..4ff9f01ddaac5 100644 --- a/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-node-selection.ts +++ b/packages/bulk-edit/src/browser/bulk-edit-tree/bulk-edit-node-selection.ts @@ -17,13 +17,14 @@ import { SelectionService } from '@theia/core/lib/common/selection-service'; import { SelectionCommandHandler } from '@theia/core/lib/common/selection-command-handler'; import { ResourceFileEdit, ResourceTextEdit } from '@theia/monaco-editor-core/esm/vs/editor/browser/services/bulkEditService'; +import { Is } from '@theia/core/lib/common/is'; export interface BulkEditNodeSelection { bulkEdit: ResourceFileEdit | ResourceTextEdit; } export namespace BulkEditNodeSelection { export function is(arg: unknown): arg is BulkEditNodeSelection { - return !!arg && typeof arg === 'object' && ('bulkEdit' in arg); + return Is.object(arg) && 'bulkEdit' in arg; } export class CommandHandler extends SelectionCommandHandler { diff --git a/packages/core/shared/reflect-metadata/index.d.ts b/packages/core/shared/reflect-metadata/index.d.ts new file mode 100644 index 0000000000000..7b873b4cc900a --- /dev/null +++ b/packages/core/shared/reflect-metadata/index.d.ts @@ -0,0 +1 @@ +export * from 'reflect-metadata'; diff --git a/packages/core/shared/reflect-metadata/index.js b/packages/core/shared/reflect-metadata/index.js new file mode 100644 index 0000000000000..2184c277f4f1a --- /dev/null +++ b/packages/core/shared/reflect-metadata/index.js @@ -0,0 +1 @@ +module.exports = require('reflect-metadata'); diff --git a/packages/core/src/browser/frontend-application.ts b/packages/core/src/browser/frontend-application.ts index 3be0d1f878139..b07809eb03307 100644 --- a/packages/core/src/browser/frontend-application.ts +++ b/packages/core/src/browser/frontend-application.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { inject, injectable, named } from 'inversify'; -import { ContributionProvider, CommandRegistry, MenuModelRegistry, isOSX, BackendStopwatch, LogLevel, Stopwatch } from '../common'; +import { ContributionProvider, CommandRegistry, MenuModelRegistry, isOSX, BackendStopwatch, LogLevel, Stopwatch, Is } from '../common'; import { MaybePromise } from '../common/types'; import { KeybindingRegistry } from './keybinding'; import { Widget } from './widgets'; @@ -101,7 +101,7 @@ export interface OnWillStopAction { export namespace OnWillStopAction { export function is(candidate: unknown): candidate is OnWillStopAction { - return typeof candidate === 'object' && !!candidate && 'action' in candidate && 'reason' in candidate; + return Is.object(candidate) && 'action' in candidate && 'reason' in candidate; } } diff --git a/packages/core/src/browser/label-parser.ts b/packages/core/src/browser/label-parser.ts index 3aa4dec730ae6..7b8a75724804b 100644 --- a/packages/core/src/browser/label-parser.ts +++ b/packages/core/src/browser/label-parser.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** import { injectable } from 'inversify'; +import { Is } from '../common/is'; export interface LabelIcon { name: string; @@ -21,8 +22,8 @@ export interface LabelIcon { } export namespace LabelIcon { - export function is(val: object): val is LabelIcon { - return 'name' in val; + export function is(val: unknown): val is LabelIcon { + return Is.object(val) && Is.string(val.name); } } diff --git a/packages/core/src/browser/label-provider.ts b/packages/core/src/browser/label-provider.ts index 48e61a057efb3..80bda25d51fbb 100644 --- a/packages/core/src/browser/label-provider.ts +++ b/packages/core/src/browser/label-provider.ts @@ -19,7 +19,7 @@ import * as fileIcons from 'file-icons-js'; import URI from '../common/uri'; import { ContributionProvider } from '../common/contribution-provider'; import { Prioritizeable } from '../common/types'; -import { Event, Emitter, Disposable, Path } from '../common'; +import { Event, Emitter, Disposable, Path, Is } from '../common'; import { FrontendApplicationContribution } from './frontend-application'; import { EnvVariablesServer } from '../common/env-variables/env-variables-protocol'; import { ResourceLabelFormatter, ResourceLabelFormatting } from '../common/label-protocol'; @@ -99,7 +99,7 @@ export interface URIIconReference { } export namespace URIIconReference { export function is(element: unknown): element is URIIconReference { - return !!element && typeof element === 'object' && 'kind' in element && (element as URIIconReference).kind === 'uriIconReference'; + return Is.object(element) && element.kind === 'uriIconReference'; } export function create(id: URIIconReference['id'], uri?: URI): URIIconReference { return { kind: 'uriIconReference', id, uri }; diff --git a/packages/core/src/browser/navigatable-types.ts b/packages/core/src/browser/navigatable-types.ts index a52f39491d139..0ba350585121a 100644 --- a/packages/core/src/browser/navigatable-types.ts +++ b/packages/core/src/browser/navigatable-types.ts @@ -17,6 +17,7 @@ import URI from '../common/uri'; import { MaybeArray } from '../common/types'; import { Widget, BaseWidget } from './widgets'; +import { Is } from '../common/is'; /** * `Navigatable` provides an access to an URI of an underlying instance of `Resource`. @@ -34,7 +35,7 @@ export interface Navigatable { export namespace Navigatable { export function is(arg: unknown): arg is Navigatable { - return !!arg && typeof arg === 'object' && 'getResourceUri' in arg && 'createMoveToUri' in arg; + return Is.object(arg) && 'getResourceUri' in arg && 'createMoveToUri' in arg; } } @@ -71,12 +72,12 @@ export namespace NavigatableWidget { } export interface NavigatableWidgetOptions { - kind: 'navigatable', - uri: string, - counter?: number, + kind: 'navigatable'; + uri: string; + counter?: number; } export namespace NavigatableWidgetOptions { export function is(arg: unknown): arg is NavigatableWidgetOptions { - return !!arg && typeof arg === 'object' && 'kind' in arg && (arg as NavigatableWidgetOptions).kind === 'navigatable'; + return Is.object(arg) && arg.kind === 'navigatable'; } } diff --git a/packages/core/src/browser/preferences/injectable-preference-proxy.ts b/packages/core/src/browser/preferences/injectable-preference-proxy.ts index b5addbf63c613..49c4afa002db8 100644 --- a/packages/core/src/browser/preferences/injectable-preference-proxy.ts +++ b/packages/core/src/browser/preferences/injectable-preference-proxy.ts @@ -16,7 +16,7 @@ import { inject, injectable, postConstruct } from 'inversify'; import { PreferenceSchema } from '../../common/preferences/preference-schema'; -import { Disposable, DisposableCollection, Emitter, Event, MaybePromise } from '../../common'; +import { Disposable, DisposableCollection, Emitter, Event, Is, MaybePromise } from '../../common'; import { PreferenceChangeEvent, PreferenceEventEmitter, PreferenceProxy, PreferenceProxyOptions, PreferenceRetrieval } from './preference-proxy'; import { PreferenceChange, PreferenceChangeImpl, PreferenceChanges, PreferenceScope, PreferenceService } from './preference-service'; import { JSONValue } from '@phosphor/coreutils'; @@ -103,7 +103,9 @@ export class InjectablePreferenceProxy> impl } get(target: unknown, property: string, receiver: unknown): unknown { - if (typeof property !== 'string') { throw new Error(`Unexpected property: ${String(property)}`); } + if (typeof property !== 'string') { + throw new Error(`Unexpected property: ${String(property)}`); + } const preferenceName = this.prefix + property; if (this.schema && (this.isFlat || !property.includes('.')) && this.schema.properties[preferenceName]) { const { overrideIdentifier } = this; @@ -143,7 +145,7 @@ export class InjectablePreferenceProxy> impl } while (parentSegment && value === undefined); let segment; - while (typeof value === 'object' && (segment = segments.pop())) { + while (Is.object(value) && (segment = segments.pop())) { value = value[segment]; } return segments.length ? undefined : value; diff --git a/packages/core/src/browser/preferences/preference-contribution.ts b/packages/core/src/browser/preferences/preference-contribution.ts index 568baa849d1f5..6cf16349997e0 100644 --- a/packages/core/src/browser/preferences/preference-contribution.ts +++ b/packages/core/src/browser/preferences/preference-contribution.ts @@ -16,7 +16,7 @@ import * as Ajv from 'ajv'; import { inject, injectable, interfaces, named, postConstruct } from 'inversify'; -import { ContributionProvider, bindContributionProvider, Emitter, Event, Disposable } from '../../common'; +import { ContributionProvider, bindContributionProvider, Emitter, Event, Disposable, Is } from '../../common'; import { PreferenceScope } from './preference-scope'; import { PreferenceProvider, PreferenceProviderDataChange } from './preference-provider'; import { @@ -80,7 +80,7 @@ export interface FrontendApplicationPreferenceConfig extends FrontendApplication } export namespace FrontendApplicationPreferenceConfig { export function is(config: FrontendApplicationConfig): config is FrontendApplicationPreferenceConfig { - return 'preferences' in config && typeof config['preferences'] === 'object'; + return Is.object(config.preferences); } } diff --git a/packages/core/src/browser/preferences/preference-language-override-service.ts b/packages/core/src/browser/preferences/preference-language-override-service.ts index 37a3cfddd1c58..aa2e346dc5697 100644 --- a/packages/core/src/browser/preferences/preference-language-override-service.ts +++ b/packages/core/src/browser/preferences/preference-language-override-service.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { injectable } from 'inversify'; -import { escapeRegExpCharacters } from '../../common'; +import { escapeRegExpCharacters, Is } from '../../common'; import { PreferenceSchemaProperties } from '../../common/preferences/preference-schema'; export interface OverridePreferenceName { @@ -24,7 +24,7 @@ export interface OverridePreferenceName { } export namespace OverridePreferenceName { export function is(arg: unknown): arg is OverridePreferenceName { - return !!arg && typeof arg === 'object' && 'preferenceName' in arg && 'overrideIdentifier' in arg; + return Is.object(arg) && 'preferenceName' in arg && 'overrideIdentifier' in arg; } } diff --git a/packages/core/src/browser/preferences/preference-provider.ts b/packages/core/src/browser/preferences/preference-provider.ts index 256d5da180859..1b1bf43a134fd 100644 --- a/packages/core/src/browser/preferences/preference-provider.ts +++ b/packages/core/src/browser/preferences/preference-provider.ts @@ -20,7 +20,7 @@ import debounce = require('p-debounce'); import { injectable, inject } from 'inversify'; import { JSONExt, JSONValue } from '@phosphor/coreutils'; import URI from '../../common/uri'; -import { Disposable, DisposableCollection, Emitter, Event } from '../../common'; +import { Disposable, DisposableCollection, Emitter, Event, Is } from '../../common'; import { Deferred } from '../../common/promise-util'; import { PreferenceScope } from './preference-scope'; import { PreferenceLanguageOverrideService } from './preference-language-override-service'; @@ -253,16 +253,12 @@ export abstract class PreferenceProvider implements Disposable { protected getParsedContent(jsonData: any): { [key: string]: any } { const preferences: { [key: string]: any } = {}; - if (typeof jsonData !== 'object') { + if (!Is.object(jsonData)) { return preferences; } - // eslint-disable-next-line guard-for-in - for (const preferenceName in jsonData) { - const preferenceValue = jsonData[preferenceName]; + for (const [preferenceName, preferenceValue] of Object.entries(jsonData)) { if (this.preferenceOverrideService.testOverrideValue(preferenceName, preferenceValue)) { - // eslint-disable-next-line guard-for-in - for (const overriddenPreferenceName in preferenceValue) { - const overriddenValue = preferenceValue[overriddenPreferenceName]; + for (const [overriddenPreferenceName, overriddenValue] of Object.entries(preferenceValue)) { preferences[`${preferenceName}.${overriddenPreferenceName}`] = overriddenValue; } } else { diff --git a/packages/core/src/browser/preferences/preference-proxy.ts b/packages/core/src/browser/preferences/preference-proxy.ts index 7a266c9532311..efdf0831cce50 100644 --- a/packages/core/src/browser/preferences/preference-proxy.ts +++ b/packages/core/src/browser/preferences/preference-proxy.ts @@ -16,7 +16,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Disposable, Event, MaybePromise } from '../../common'; +import { Disposable, Event, Is, MaybePromise } from '../../common'; import { PreferenceService } from './preference-service'; import { PreferenceSchema } from './preference-contribution'; import { PreferenceScope } from './preference-scope'; @@ -332,7 +332,7 @@ export function createPreferenceProxy(preferences: PreferenceService, promise } while (parentSegment && value === undefined); let segment; - while (typeof value === 'object' && (segment = segments.pop())) { + while (Is.object(value) && (segment = segments.pop())) { value = value[segment]; } return segments.length ? undefined : value; diff --git a/packages/core/src/browser/saveable.ts b/packages/core/src/browser/saveable.ts index 06f5571c18140..c306bd32f75e4 100644 --- a/packages/core/src/browser/saveable.ts +++ b/packages/core/src/browser/saveable.ts @@ -22,6 +22,7 @@ import { Key } from './keyboard/keys'; import { AbstractDialog } from './dialogs'; import { waitForClosed } from './widgets'; import { nls } from '../common/nls'; +import { Is } from '../common/is'; export interface Saveable { readonly dirty: boolean; @@ -60,10 +61,10 @@ export namespace Saveable { export type Snapshot = { value: string } | { read(): string | null }; export function isSource(arg: unknown): arg is SaveableSource { - return typeof arg === 'object' && !!arg && is((arg as SaveableSource).saveable); + return Is.object(arg) && is(arg.saveable); } export function is(arg: unknown): arg is Saveable { - return typeof arg === 'object' && !!arg && 'dirty' in arg && 'onDirtyChanged' in arg; + return Is.object(arg) && 'dirty' in arg && 'onDirtyChanged' in arg; } export function get(arg: unknown): Saveable | undefined { if (is(arg)) { diff --git a/packages/core/src/browser/shell/application-shell.ts b/packages/core/src/browser/shell/application-shell.ts index 272a06bfde5f8..6c00418c2ce7d 100644 --- a/packages/core/src/browser/shell/application-shell.ts +++ b/packages/core/src/browser/shell/application-shell.ts @@ -22,7 +22,7 @@ import { } from '@phosphor/widgets'; import { Message } from '@phosphor/messaging'; import { IDragEvent } from '@phosphor/dragdrop'; -import { RecursivePartial, Event as CommonEvent, DisposableCollection, Disposable, environment } from '../../common'; +import { RecursivePartial, Event as CommonEvent, DisposableCollection, Disposable, environment, Is } from '../../common'; import { animationFrame } from '../browser'; import { Saveable, SaveableWidget, SaveOptions, SaveableSource } from '../saveable'; import { StatusBarImpl, StatusBarEntry, StatusBarAlignment } from '../status-bar/status-bar'; @@ -2101,7 +2101,7 @@ export namespace ApplicationShell { export namespace TrackableWidgetProvider { export function is(widget: unknown): widget is TrackableWidgetProvider { - return !!widget && typeof widget === 'object' && 'getTrackableWidgets' in widget; + return Is.object(widget) && 'getTrackableWidgets' in widget; } } diff --git a/packages/core/src/browser/shell/shell-layout-restorer.ts b/packages/core/src/browser/shell/shell-layout-restorer.ts index ac7f680ff70fe..9e138923252c6 100644 --- a/packages/core/src/browser/shell/shell-layout-restorer.ts +++ b/packages/core/src/browser/shell/shell-layout-restorer.ts @@ -28,6 +28,7 @@ import { ApplicationShell, applicationShellLayoutVersion, ApplicationShellLayout import { CommonCommands } from '../common-frontend-contribution'; import { WindowService } from '../window/window-service'; import { StopReason } from '../../common/frontend-application-state'; +import { Is } from '../../common/is'; /** * A contract for widgets that want to store and restore their inner state, between sessions. @@ -47,7 +48,7 @@ export interface StatefulWidget { export namespace StatefulWidget { export function is(arg: unknown): arg is StatefulWidget { - return !!arg && typeof arg === 'object' && typeof (arg as StatefulWidget).storeState === 'function' && typeof (arg as StatefulWidget).restoreState === 'function'; + return Is.object(arg) && Is.func(arg.storeState) && Is.func(arg.restoreState); } } @@ -232,11 +233,7 @@ export class ShellLayoutRestorer implements CommandContribution { const parseContext = new ShellLayoutRestorer.ParseContext(); const layout = this.parse(layoutData, parseContext); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let layoutVersion: number | any; - try { - layoutVersion = 'version' in layout && Number(layout.version); - } catch { /* no-op */ } + const layoutVersion = Number(layout.version); if (typeof layoutVersion !== 'number' || Number.isNaN(layoutVersion)) { throw new Error('could not resolve a layout version'); } @@ -282,9 +279,8 @@ export class ShellLayoutRestorer implements CommandContribution { }); } return widgets; - } else if (value && typeof value === 'object' && !Array.isArray(value)) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const copy: any = {}; + } else if (Is.object>(value) && !Array.isArray(value)) { + const copy: Record = {}; for (const p in value) { if (this.isWidgetProperty(p)) { parseContext.push(async context => { @@ -306,7 +302,7 @@ export class ShellLayoutRestorer implements CommandContribution { // don't catch exceptions, if one migration fails all should fail. const migrated = await migration.onWillInflateWidget(desc, context); if (migrated) { - if (migrated.innerWidgetState && typeof migrated.innerWidgetState !== 'string') { + if (Is.object(migrated.innerWidgetState)) { // in order to inflate nested widgets migrated.innerWidgetState = JSON.stringify(migrated.innerWidgetState); } diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts index acf15dd917bbc..1e9f9a9b6010c 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-types.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import * as React from 'react'; -import { ArrayUtils, Event, MenuPath } from '../../../common'; +import { ArrayUtils, Event, Is, MenuPath } from '../../../common'; import { Widget } from '../../widgets'; /** Items whose group is exactly 'navigation' will be rendered inline. */ @@ -27,13 +27,9 @@ export interface TabBarDelegator extends Widget { } export namespace TabBarDelegator { - export const is = (candidate?: Widget): candidate is TabBarDelegator => { - if (candidate) { - const asDelegator = candidate as TabBarDelegator; - return typeof asDelegator.getTabBarDelegate === 'function'; - } - return false; - }; + export function is(candidate?: Widget): candidate is TabBarDelegator { + return Is.object(candidate) && Is.func(candidate.getTabBarDelegate); + } } interface RegisteredToolbarItem { @@ -172,14 +168,13 @@ export namespace TabBarToolbarItem { }; export function is(arg: unknown): arg is TabBarToolbarItem { - return !!arg && typeof arg === 'object' && 'command' in arg && typeof (arg as TabBarToolbarItem).command === 'string'; + return Is.object(arg) && Is.string(arg.command); } } export namespace MenuToolbarItem { export function getMenuPath(item: AnyToolbarItem): MenuPath | undefined { - const asDelegate = item as MenuToolbarItem; - return Array.isArray(asDelegate.menuPath) ? asDelegate.menuPath : undefined; + return Array.isArray(item.menuPath) ? item.menuPath : undefined; } } diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx index 7776842ebf958..dc5a8975743c3 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx @@ -109,7 +109,7 @@ export class TabBarToolbar extends ReactWidget { const classNames = []; if (item.text) { for (const labelPart of this.labelParser.parse(item.text)) { - if (typeof labelPart !== 'string' && LabelIcon.is(labelPart)) { + if (LabelIcon.is(labelPart)) { const className = `fa fa-${labelPart.name}${labelPart.animation ? ' fa-' + labelPart.animation : ''}`; classNames.push(...className.split(' ')); } else { diff --git a/packages/core/src/browser/source-tree/tree-source.ts b/packages/core/src/browser/source-tree/tree-source.ts index e0dc628eeabe8..9251f5cd427e6 100644 --- a/packages/core/src/browser/source-tree/tree-source.ts +++ b/packages/core/src/browser/source-tree/tree-source.ts @@ -22,6 +22,7 @@ import { Emitter, Event } from '../../common/event'; import { MaybePromise } from '../../common/types'; import { Disposable, DisposableCollection } from '../../common/disposable'; import { TreeWidget } from '../tree'; +import { Is } from '../../common/is'; export interface TreeElement { /** default: parent id + position among siblings */ @@ -39,7 +40,7 @@ export interface CompositeTreeElement extends TreeElement { } export namespace CompositeTreeElement { export function is(element: unknown): element is CompositeTreeElement { - return !!element && typeof element === 'object' && 'getElements' in element; + return Is.object(element) && 'getElements' in element; } export function hasElements(element: unknown): element is CompositeTreeElement { return is(element) && element.hasElements !== false; diff --git a/packages/core/src/browser/status-bar/status-bar.tsx b/packages/core/src/browser/status-bar/status-bar.tsx index 4d24eb4a8e130..2400710d0fff3 100644 --- a/packages/core/src/browser/status-bar/status-bar.tsx +++ b/packages/core/src/browser/status-bar/status-bar.tsx @@ -161,7 +161,7 @@ export class StatusBarImpl extends ReactWidget implements StatusBar { const children: JSX.Element[] = []; childStrings.forEach((val, key) => { - if (!(typeof val === 'string') && LabelIcon.is(val)) { + if (LabelIcon.is(val)) { const animation = val.animation ? ` fa-${val.animation}` : ''; if (val.name.startsWith('codicon-')) { children.push(); diff --git a/packages/core/src/browser/tree/tree-selection.ts b/packages/core/src/browser/tree/tree-selection.ts index d358008b91ba7..34cef93f3bb11 100644 --- a/packages/core/src/browser/tree/tree-selection.ts +++ b/packages/core/src/browser/tree/tree-selection.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { TreeNode } from './tree'; -import { Event, Disposable, SelectionProvider } from '../../common'; +import { Event, Disposable, SelectionProvider, Is } from '../../common'; /** * The tree selection service. @@ -87,7 +87,7 @@ export namespace TreeSelection { } export function is(arg: unknown): arg is TreeSelection { - return !!arg && typeof arg === 'object' && 'node' in arg; + return Is.object(arg) && 'node' in arg; } export function isRange(arg: TreeSelection | SelectionType | undefined): boolean { diff --git a/packages/core/src/browser/tree/tree.ts b/packages/core/src/browser/tree/tree.ts index 8a65875b61499..0ca750bec6730 100644 --- a/packages/core/src/browser/tree/tree.ts +++ b/packages/core/src/browser/tree/tree.ts @@ -20,6 +20,7 @@ import { Disposable, DisposableCollection } from '../../common/disposable'; import { CancellationToken, CancellationTokenSource } from '../../common/cancellation'; import { Mutable } from '../../common/types'; import { timeout } from '../../common/promise-util'; +import { Is } from '../../common/is'; export const Tree = Symbol('Tree'); @@ -124,7 +125,7 @@ export interface TreeNode { export namespace TreeNode { export function is(node: unknown): node is TreeNode { - return !!node && typeof node === 'object' && 'id' in node && 'parent' in node; + return Is.object(node) && 'id' in node && 'parent' in node; } export function equals(left: TreeNode | undefined, right: TreeNode | undefined): boolean { @@ -148,7 +149,7 @@ export interface CompositeTreeNode extends TreeNode { export namespace CompositeTreeNode { export function is(node: unknown): node is CompositeTreeNode { - return typeof node === 'object' && !!node && 'children' in node; + return Is.object(node) && 'children' in node; } export function getFirstChild(parent: CompositeTreeNode): TreeNode | undefined { diff --git a/packages/core/src/browser/view-container.ts b/packages/core/src/browser/view-container.ts index 3adc265d73857..3ec73cdff7aae 100644 --- a/packages/core/src/browser/view-container.ts +++ b/packages/core/src/browser/view-container.ts @@ -30,7 +30,7 @@ import { FrontendApplicationStateService } from './frontend-application-state'; import { ContextMenuRenderer, Anchor } from './context-menu-renderer'; import { parseCssMagnitude } from './browser'; import { TabBarToolbarRegistry, TabBarToolbarFactory, TabBarToolbar, TabBarDelegator, TabBarToolbarItem } from './shell/tab-bar-toolbar'; -import { isEmpty, nls } from '../common'; +import { isEmpty, Is, nls } from '../common'; import { WidgetManager } from './widget-manager'; import { Key } from './keys'; import { ProgressBarFactory } from './progress-bar-factory'; @@ -64,13 +64,13 @@ export interface BadgeWidget { export namespace DescriptionWidget { export function is(arg: unknown): arg is DescriptionWidget { - return !!arg && typeof arg === 'object' && 'onDidChangeDescription' in arg; + return Is.object(arg) && 'onDidChangeDescription' in arg; } } export namespace BadgeWidget { export function is(arg: unknown): arg is BadgeWidget { - return !!arg && typeof arg === 'object' && 'onDidChangeBadge' in arg; + return Is.object(arg) && 'onDidChangeBadge' in arg; } } diff --git a/packages/core/src/browser/widgets/widget.ts b/packages/core/src/browser/widgets/widget.ts index 5274d0f2386a6..c29ab8074787f 100644 --- a/packages/core/src/browser/widgets/widget.ts +++ b/packages/core/src/browser/widgets/widget.ts @@ -19,7 +19,7 @@ import { injectable, decorate, unmanaged } from 'inversify'; import { Title, Widget } from '@phosphor/widgets'; import { Message, MessageLoop } from '@phosphor/messaging'; -import { Emitter, Event, Disposable, DisposableCollection, MaybePromise } from '../../common'; +import { Emitter, Event, Disposable, DisposableCollection, MaybePromise, Is } from '../../common'; import { KeyCode, KeysOrKeyCodes } from '../keyboard/keys'; import PerfectScrollbar from 'perfect-scrollbar'; @@ -247,7 +247,7 @@ export interface EventListenerObject { } export namespace EventListenerObject { export function is(listener: unknown): listener is EventListenerObject { - return !!listener && typeof listener === 'object' && 'handleEvent' in listener; + return Is.object(listener) && 'handleEvent' in listener; } } export type EventListenerOrEventListenerObject = EventListener | EventListenerObject; diff --git a/packages/core/src/common/cancellation.ts b/packages/core/src/common/cancellation.ts index 05e93cc18d748..4f48e8aa9ef20 100644 --- a/packages/core/src/common/cancellation.ts +++ b/packages/core/src/common/cancellation.ts @@ -19,6 +19,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from './event'; +import { Is } from './is'; export interface CancellationToken { readonly isCancellationRequested: boolean; @@ -51,10 +52,9 @@ export namespace CancellationToken { }); export function is(value: unknown): value is CancellationToken { - const candidate = value as CancellationToken; - return candidate && (candidate === CancellationToken.None - || candidate === CancellationToken.Cancelled - || (typeof candidate.isCancellationRequested === 'boolean' && !!candidate.onCancellationRequested)); + return Is.object(value) && (value === CancellationToken.None + || value === CancellationToken.Cancelled + || (Is.boolean(value.isCancellationRequested) && !!value.onCancellationRequested)); } } diff --git a/packages/core/src/common/command.ts b/packages/core/src/common/command.ts index b46a29a510305..4a5bcdcc6eae3 100644 --- a/packages/core/src/common/command.ts +++ b/packages/core/src/common/command.ts @@ -20,6 +20,7 @@ import { Disposable, DisposableCollection } from './disposable'; import { ContributionProvider } from './contribution-provider'; import { nls } from './nls'; import debounce = require('p-debounce'); +import { Is } from './is'; /** * A command is a unique identifier of a function @@ -50,7 +51,7 @@ export interface Command { export namespace Command { /* Determine whether object is a Command */ export function is(arg: unknown): arg is Command { - return !!arg && typeof arg === 'object' && 'id' in arg; + return Is.object(arg) && 'id' in arg; } /** Utility function to easily translate commands */ diff --git a/packages/core/src/common/disposable.ts b/packages/core/src/common/disposable.ts index 379b31006f17f..4d038ea74caa6 100644 --- a/packages/core/src/common/disposable.ts +++ b/packages/core/src/common/disposable.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** import { Event, Emitter } from './event'; +import { Is } from './is'; export interface Disposable { /** @@ -24,7 +25,7 @@ export interface Disposable { export namespace Disposable { export function is(arg: unknown): arg is Disposable { - return !!arg && typeof arg === 'object' && typeof (arg as Disposable).dispose === 'function'; + return Is.object(arg) && Is.func(arg.dispose); } export function create(func: () => void): Disposable { return { dispose: func }; diff --git a/packages/core/src/common/index.ts b/packages/core/src/common/index.ts index cedfd254d0009..147cab433f6e4 100644 --- a/packages/core/src/common/index.ts +++ b/packages/core/src/common/index.ts @@ -43,6 +43,7 @@ export * from './contribution-filter'; export * from './nls'; export * from './numbers'; export * from './performance'; +export * from './is'; import { environment } from '@theia/application-package/lib/environment'; export { environment }; diff --git a/packages/core/src/common/is.ts b/packages/core/src/common/is.ts new file mode 100644 index 0000000000000..ca8b949af1f96 --- /dev/null +++ b/packages/core/src/common/is.ts @@ -0,0 +1,55 @@ +// ***************************************************************************** +// Copyright (C) 2022 TypeFox and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0. +// +// This Source Code may also be made available under the following Secondary +// Licenses when the conditions for such availability set forth in the Eclipse +// Public License v. 2.0 are satisfied: GNU General Public License, version 2 +// with the GNU Classpath Exception which is available at +// https://www.gnu.org/software/classpath/license.html. +// +// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +// ***************************************************************************** + +export namespace Is { + + export function boolean(value: unknown): value is boolean { + return value === true || value === false; + } + + export function string(value: unknown): value is string { + return typeof value === 'string' || value instanceof String; + } + + export function number(value: unknown): value is number { + return typeof value === 'number' || value instanceof Number; + } + + export function error(value: unknown): value is Error { + return value instanceof Error; + } + + export function func(value: unknown): value is Function { + return typeof value === 'function'; + } + + export function stringArray(value: unknown): value is string[] { + return Array.isArray(value) && value.every(elem => string(elem)); + } + + export function typedArray(value: unknown, check: (value: unknown) => boolean): value is T[] { + return Array.isArray(value) && value.every(check); + } + + export function object>(value: unknown): value is T { + return typeof value === 'object' && !!value; + } + + export function undefined(value: unknown): value is undefined { + return typeof value === 'undefined'; + } + +} diff --git a/packages/core/src/common/keybinding.ts b/packages/core/src/common/keybinding.ts index cb334bfd7bc28..2037779b6c701 100644 --- a/packages/core/src/common/keybinding.ts +++ b/packages/core/src/common/keybinding.ts @@ -13,6 +13,9 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** + +import { Is } from './is'; + /** * A Keybinding binds a specific key sequence ({@link Keybinding#keybinding}) to trigger a command ({@link Keybinding#command}). A Keybinding optionally may * define a "when clause" ({@link Keybinding#when}) to specify in which context it becomes active. @@ -102,7 +105,7 @@ export namespace Keybinding { /* Determine whether object is a KeyBinding */ export function is(arg: unknown): arg is Keybinding { - return !!arg && typeof arg === 'object' && 'command' in arg && 'keybinding' in arg; + return Is.object(arg) && 'command' in arg && 'keybinding' in arg; } } @@ -118,6 +121,6 @@ export interface RawKeybinding extends Omit { export namespace RawKeybinding { export function is(candidate: unknown): candidate is RawKeybinding { - return typeof candidate === 'object' && !!candidate && 'command' in candidate && 'key' in candidate; + return Is.object(candidate) && 'command' in candidate && 'key' in candidate; } } diff --git a/packages/core/src/common/keys.ts b/packages/core/src/common/keys.ts index 156c2319b3bd9..945a6d184b4ef 100644 --- a/packages/core/src/common/keys.ts +++ b/packages/core/src/common/keys.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Is } from './is'; import { isOSX } from './os'; export type KeySequence = KeyCode[]; @@ -489,7 +490,7 @@ export namespace SpecialCases { export namespace Key { export function isKey(arg: unknown): arg is Key { - return !!arg && typeof arg === 'object' && 'code' in arg && 'keyCode' in arg; + return Is.object(arg) && 'code' in arg && 'keyCode' in arg; } export function getKey(arg: string | number): Key | undefined { diff --git a/packages/core/src/common/lsp-types.ts b/packages/core/src/common/lsp-types.ts index 7af131ef28ae9..a3256046f44f1 100644 --- a/packages/core/src/common/lsp-types.ts +++ b/packages/core/src/common/lsp-types.ts @@ -15,6 +15,7 @@ // ***************************************************************************** import { Range } from 'vscode-languageserver-protocol'; +import { Is } from './is'; export interface TextDocumentContentChangeDelta { readonly range: Range; @@ -24,12 +25,10 @@ export interface TextDocumentContentChangeDelta { export namespace TextDocumentContentChangeDelta { export function is(arg: unknown): arg is TextDocumentContentChangeDelta { - const changeDelta = arg as TextDocumentContentChangeDelta; - return !!changeDelta - && typeof changeDelta === 'object' - && typeof changeDelta.text === 'string' - && (typeof changeDelta.rangeLength === 'number' || typeof changeDelta.rangeLength === 'undefined') - && Range.is(changeDelta.range); + return Is.object(arg) + && Is.string(arg.text) + && (Is.number(arg.rangeLength) || Is.undefined(arg.rangeLength)) + && Range.is(arg.range); } } diff --git a/packages/core/src/common/markdown-rendering/markdown-string.ts b/packages/core/src/common/markdown-rendering/markdown-string.ts index 5bb94a4a23553..d745000b9cbea 100644 --- a/packages/core/src/common/markdown-rendering/markdown-string.ts +++ b/packages/core/src/common/markdown-rendering/markdown-string.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Is } from '../is'; import { escapeRegExpCharacters } from '../strings'; import { UriComponents } from '../uri'; import { escapeIcons } from './icon-utilities'; @@ -37,8 +38,7 @@ export namespace MarkdownString { * @returns whether the candidate satisfies the interface of a markdown string */ export function is(candidate: unknown): candidate is MarkdownString { - const maybeMarkdownString = candidate as MarkdownString; - return typeof maybeMarkdownString === 'object' && !!maybeMarkdownString && typeof maybeMarkdownString.value === 'string'; + return Is.object(candidate) && Is.string(candidate.value); } } diff --git a/packages/core/src/common/menu/menu-types.ts b/packages/core/src/common/menu/menu-types.ts index c763c26a8e649..e5b0479cdab3f 100644 --- a/packages/core/src/common/menu/menu-types.ts +++ b/packages/core/src/common/menu/menu-types.ts @@ -14,6 +14,8 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Is } from '../is'; + /** * A menu entry representing an action, e.g. "New File". */ @@ -37,7 +39,7 @@ export interface MenuAction extends MenuNodeRenderingData, Pick(obj: T): T { - if (!obj || typeof obj !== 'object') { + if (!Is.object(obj)) { return obj; } if (obj instanceof RegExp) { @@ -24,9 +26,8 @@ export function deepClone(obj: T): T { // eslint-disable-next-line @typescript-eslint/no-explicit-any const result: any = Array.isArray(obj) ? [] : {}; Object.keys(obj).forEach((key: string) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const prop = (obj)[key]; - if (prop && typeof prop === 'object') { + const prop = obj[key]; + if (Is.object(prop)) { result[key] = deepClone(prop); } else { result[key] = prop; @@ -36,7 +37,7 @@ export function deepClone(obj: T): T { } export function deepFreeze(obj: T): T { - if (!obj || typeof obj !== 'object') { + if (!Is.object(obj)) { return obj; } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -47,7 +48,7 @@ export function deepFreeze(obj: T): T { for (const key in objectToFreeze) { if (_hasOwnProperty.call(objectToFreeze, key)) { const prop = objectToFreeze[key]; - if (typeof prop === 'object' && !Object.isFrozen(prop)) { + if (Is.object(prop) && !Object.isFrozen(prop)) { stack.push(prop); } } diff --git a/packages/core/src/common/preferences/preference-schema.ts b/packages/core/src/common/preferences/preference-schema.ts index f5a41cf50faca..2fbba9e4f78b4 100644 --- a/packages/core/src/common/preferences/preference-schema.ts +++ b/packages/core/src/common/preferences/preference-schema.ts @@ -17,6 +17,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { JSONValue } from '@phosphor/coreutils'; +import { Is } from '../is'; import { IJSONSchema } from '../json-schema'; import { PreferenceScope } from './preference-scope'; @@ -28,7 +29,7 @@ export interface PreferenceSchema { } export namespace PreferenceSchema { export function is(obj: unknown): obj is PreferenceSchema { - return !!obj && typeof obj === 'object' && ('properties' in obj) && PreferenceSchemaProperties.is((obj as PreferenceSchema).properties); + return Is.object(obj) && PreferenceSchemaProperties.is(obj.properties); } export function getDefaultScope(schema: PreferenceSchema): PreferenceScope { let defaultScope: PreferenceScope = PreferenceScope.Workspace; @@ -46,7 +47,7 @@ export interface PreferenceSchemaProperties { } export namespace PreferenceSchemaProperties { export function is(obj: unknown): obj is PreferenceSchemaProperties { - return !!obj && typeof obj === 'object'; + return Is.object(obj); } } @@ -86,7 +87,7 @@ export namespace PreferenceDataProperty { export function fromPreferenceSchemaProperty(schemaProps: PreferenceSchemaProperty, defaultScope: PreferenceScope = PreferenceScope.Workspace): PreferenceDataProperty { if (!schemaProps.scope) { schemaProps.scope = defaultScope; - } else if (typeof schemaProps.scope === 'string') { + } else if (Is.string(schemaProps.scope)) { return Object.assign(schemaProps, { scope: PreferenceScope.fromString(schemaProps.scope) || defaultScope }); } return schemaProps; diff --git a/packages/core/src/common/promise-util.ts b/packages/core/src/common/promise-util.ts index 301ac51254229..579c991565cff 100644 --- a/packages/core/src/common/promise-util.ts +++ b/packages/core/src/common/promise-util.ts @@ -17,6 +17,7 @@ import { Disposable } from './disposable'; import { Event } from './event'; import { CancellationToken, CancellationError, cancelled } from './cancellation'; +import { Is } from './is'; /** * Simple implementation of the deferred pattern. @@ -126,5 +127,5 @@ export function waitForEvent(event: Event, ms: number, thisArg?: any, disp } export function isThenable(obj: unknown): obj is Promise { - return typeof obj === 'object' && !!obj && typeof (obj as Promise).then === 'function'; + return Is.object>(obj) && Is.func(obj.then); } diff --git a/packages/core/src/common/selection.ts b/packages/core/src/common/selection.ts index 62deee5e06a5e..0f59a90d2ce43 100644 --- a/packages/core/src/common/selection.ts +++ b/packages/core/src/common/selection.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Is } from './is'; import URI from './uri'; export interface UriSelection { @@ -23,7 +24,7 @@ export interface UriSelection { export namespace UriSelection { export function is(arg: unknown): arg is UriSelection { - return !!arg && typeof arg === 'object' && ('uri' in arg) && (arg as UriSelection).uri instanceof URI; + return Is.object(arg) && arg.uri instanceof URI; } export function getUri(selection: unknown): URI | undefined { diff --git a/packages/core/src/common/uri-command-handler.ts b/packages/core/src/common/uri-command-handler.ts index a15967600d7c5..7d51f20efe11e 100644 --- a/packages/core/src/common/uri-command-handler.ts +++ b/packages/core/src/common/uri-command-handler.ts @@ -21,6 +21,7 @@ import { UriSelection } from '../common/selection'; import { CommandHandler } from './command'; import URI from './uri'; import { MaybeArray } from './types'; +import { Is } from './is'; export interface UriCommandHandler> extends CommandHandler { @@ -59,7 +60,7 @@ export class UriAwareCommandHandler> implements UriCom protected getUri(...args: any[]): T | undefined { const [maybeUriArray] = args; const firstArgIsOK = this.isMulti() - ? Array.isArray(maybeUriArray) && maybeUriArray.every(uri => uri instanceof URI) + ? Is.typedArray(maybeUriArray, uri => uri instanceof URI) : maybeUriArray instanceof URI; if (firstArgIsOK) { diff --git a/packages/core/src/electron-node/token/electron-token-validator.ts b/packages/core/src/electron-node/token/electron-token-validator.ts index 6b58d4b009472..9ab17dd1edddf 100644 --- a/packages/core/src/electron-node/token/electron-token-validator.ts +++ b/packages/core/src/electron-node/token/electron-token-validator.ts @@ -18,7 +18,7 @@ import * as http from 'http'; import * as cookie from 'cookie'; import * as crypto from 'crypto'; import { injectable, postConstruct } from 'inversify'; -import { MaybePromise } from '../../common'; +import { Is, MaybePromise } from '../../common'; import { ElectronSecurityToken } from '../../electron-common/electron-token'; import { WsRequestValidatorContribution } from '../../node/ws-request-validators'; @@ -44,9 +44,9 @@ export class ElectronTokenValidator implements WsRequestValidatorContribution { */ allowRequest(request: http.IncomingMessage): boolean { const cookieHeader = request.headers.cookie; - if (typeof cookieHeader === 'string') { + if (Is.string(cookieHeader)) { const token = cookie.parse(cookieHeader)[ElectronSecurityToken]; - if (typeof token === 'string') { + if (Is.string(token)) { return this.isTokenValid(JSON.parse(token)); } } @@ -60,10 +60,8 @@ export class ElectronTokenValidator implements WsRequestValidatorContribution { * * @param token Parsed object sent by the client as the token. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - isTokenValid(token: any): boolean { - // eslint-disable-next-line no-null/no-null - if (typeof token === 'object' && token !== null && typeof token.value === 'string') { + isTokenValid(token: unknown): boolean { + if (Is.object(token) && Is.string(token.value)) { try { const received = Buffer.from(token.value, 'utf8'); const expected = Buffer.from(this.electronSecurityToken!.value, 'utf8'); diff --git a/packages/core/src/node/i18n/localization-contribution.ts b/packages/core/src/node/i18n/localization-contribution.ts index 078fb30970c09..5a87189bb7635 100644 --- a/packages/core/src/node/i18n/localization-contribution.ts +++ b/packages/core/src/node/i18n/localization-contribution.ts @@ -16,7 +16,7 @@ import * as fs from 'fs-extra'; import { inject, injectable, named } from 'inversify'; -import { ContributionProvider } from '../../common'; +import { ContributionProvider, Is } from '../../common'; import { LanguageInfo, Localization } from '../../common/i18n/localization'; import { LocalizationProvider } from './localization-provider'; @@ -79,12 +79,12 @@ export class LocalizationRegistry { } protected flattenTranslations(localization: unknown): Record { - if (typeof localization === 'object' && localization) { + if (Is.object(localization)) { const record: Record = {}; for (const [key, value] of Object.entries(localization)) { if (typeof value === 'string') { record[key] = value; - } else if (value && typeof value === 'object') { + } else if (Is.object(value)) { const flattened = this.flattenTranslations(value); for (const [flatKey, flatValue] of Object.entries(flattened)) { record[`${key}/${flatKey}`] = flatValue; diff --git a/packages/debug/src/browser/breakpoint/breakpoint-marker.ts b/packages/debug/src/browser/breakpoint/breakpoint-marker.ts index 74e45500b9a43..615acd0871359 100644 --- a/packages/debug/src/browser/breakpoint/breakpoint-marker.ts +++ b/packages/debug/src/browser/breakpoint/breakpoint-marker.ts @@ -18,6 +18,7 @@ import { UUID } from '@theia/core/shared/@phosphor/coreutils'; import URI from '@theia/core/lib/common/uri'; import { Marker } from '@theia/markers/lib/common/marker'; import { DebugProtocol } from '@vscode/debugprotocol/lib/debugProtocol'; +import { Is } from '@theia/core/lib/common/is'; export const BREAKPOINT_KIND = 'breakpoint'; @@ -96,8 +97,7 @@ export namespace InstructionBreakpoint { }; } - export function is(thing: BaseBreakpoint): thing is InstructionBreakpoint { - const candidate = thing as InstructionBreakpoint; - return 'instructionReference' in candidate && typeof candidate.instructionReference === 'string'; + export function is(arg: BaseBreakpoint): arg is InstructionBreakpoint { + return Is.object(arg) && Is.string(arg.instructionReference); } } diff --git a/packages/debug/src/browser/debug-configuration-model.ts b/packages/debug/src/browser/debug-configuration-model.ts index 90260b3aedf6f..718d72c9aa5fd 100644 --- a/packages/debug/src/browser/debug-configuration-model.ts +++ b/packages/debug/src/browser/debug-configuration-model.ts @@ -20,6 +20,7 @@ import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposa import { DebugConfiguration } from '../common/debug-common'; import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service'; import { DebugCompound } from '../common/debug-compound'; +import { Is } from '@theia/core/lib/common/is'; export class DebugConfigurationModel implements Disposable { @@ -71,7 +72,7 @@ export class DebugConfigurationModel implements Disposable { const configurations: DebugConfiguration[] = []; // eslint-disable-next-line @typescript-eslint/no-explicit-any const { configUri, value } = this.preferences.resolve('launch', undefined, this.workspaceFolderUri); - if (value && typeof value === 'object' && Array.isArray(value.configurations)) { + if (Is.object(value) && Array.isArray(value.configurations)) { for (const configuration of value.configurations) { if (DebugConfiguration.is(configuration)) { configurations.push(configuration); @@ -79,7 +80,7 @@ export class DebugConfigurationModel implements Disposable { } } const compounds: DebugCompound[] = []; - if (value && typeof value === 'object' && Array.isArray(value.compounds)) { + if (Is.object(value) && Array.isArray(value.compounds)) { for (const compound of value.compounds) { if (DebugCompound.is(compound)) { compounds.push(compound); diff --git a/packages/debug/src/browser/editor/debug-editor-model.ts b/packages/debug/src/browser/editor/debug-editor-model.ts index 00060712ffd55..0264e0eae322c 100644 --- a/packages/debug/src/browser/editor/debug-editor-model.ts +++ b/packages/debug/src/browser/editor/debug-editor-model.ts @@ -20,6 +20,7 @@ import * as monaco from '@theia/monaco-editor-core'; import { IConfigurationService } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configuration'; import { StandaloneCodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneCodeEditor'; import { IDecorationOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/editorCommon'; +import { IEditorHoverOptions } from '@theia/monaco-editor-core/esm/vs/editor/common/config/editorOptions'; import URI from '@theia/core/lib/common/uri'; import { Disposable, DisposableCollection, MenuPath, isOSX } from '@theia/core'; import { ContextMenuRenderer } from '@theia/core/lib/browser'; @@ -146,7 +147,7 @@ export class DebugEditorModel implements Disposable { resource: model.uri, overrideIdentifier: model.getLanguageId(), }; - const { enabled, delay, sticky } = this.configurationService.getValue('editor.hover', overrides); + const { enabled, delay, sticky } = this.configurationService.getValue('editor.hover', overrides); codeEditor.updateOptions({ hover: { enabled, diff --git a/packages/debug/src/common/debug-compound.ts b/packages/debug/src/common/debug-compound.ts index d019e69b9a142..4afeecf1e538f 100644 --- a/packages/debug/src/common/debug-compound.ts +++ b/packages/debug/src/common/debug-compound.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Is } from '@theia/core/lib/common/is'; import { TaskIdentifier } from '@theia/task/lib/common'; export const defaultCompound: DebugCompound = { name: 'Compound', configurations: [] }; @@ -27,6 +28,6 @@ export interface DebugCompound { export namespace DebugCompound { export function is(arg: unknown): arg is DebugCompound { - return !!arg && typeof arg === 'object' && 'name' in arg && 'configurations' in arg; + return Is.object(arg) && 'name' in arg && 'configurations' in arg; } } diff --git a/packages/debug/src/common/debug-configuration.ts b/packages/debug/src/common/debug-configuration.ts index 6abd0a8a725d6..cfdebb279ad0f 100644 --- a/packages/debug/src/common/debug-configuration.ts +++ b/packages/debug/src/common/debug-configuration.ts @@ -13,6 +13,7 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Is } from '@theia/core/lib/common/is'; import { TaskIdentifier } from '@theia/task/lib/common'; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -72,7 +73,7 @@ export interface DebugConfiguration { } export namespace DebugConfiguration { export function is(arg: unknown): arg is DebugConfiguration { - return !!arg && typeof arg === 'object' && 'type' in arg && 'name' in arg && 'request' in arg; + return Is.object(arg) && 'type' in arg && 'name' in arg && 'request' in arg; } } diff --git a/packages/debug/src/node/debug-adapter-factory.ts b/packages/debug/src/node/debug-adapter-factory.ts index dd0b0b9026e3f..756397f10725c 100644 --- a/packages/debug/src/node/debug-adapter-factory.ts +++ b/packages/debug/src/node/debug-adapter-factory.ts @@ -41,6 +41,7 @@ import { import { DebugAdapterSessionImpl } from '../common/debug-adapter-session'; import { environment } from '@theia/core/shared/@theia/application-package'; import { ProcessDebugAdapter, SocketDebugAdapter } from './stream-debug-adapter'; +import { Is } from '@theia/core/lib/common/is'; /** * [DebugAdapterFactory](#DebugAdapterFactory) implementation based on @@ -67,7 +68,7 @@ export class LaunchBasedDebugAdapterFactory implements DebugAdapterFactory { private childProcess(executable: DebugAdapterExecutable): RawProcess { const isForkOptions = (forkOptions: unknown): forkOptions is RawForkOptions => - !!forkOptions && typeof forkOptions === 'object' && 'modulePath' in forkOptions; + Is.object(forkOptions) && 'modulePath' in forkOptions; const processOptions: RawProcessOptions | RawForkOptions = { ...executable }; const options: { stdio: (string | number)[], env?: object, execArgv?: string[] } = { stdio: ['pipe', 'pipe', 2] }; diff --git a/packages/editor/src/browser/editor.ts b/packages/editor/src/browser/editor.ts index 12a1cf85c21e2..cddc544996006 100644 --- a/packages/editor/src/browser/editor.ts +++ b/packages/editor/src/browser/editor.ts @@ -17,7 +17,7 @@ import { Position, Range, Location } from '@theia/core/shared/vscode-languageserver-protocol'; import * as lsp from '@theia/core/shared/vscode-languageserver-protocol'; import URI from '@theia/core/lib/common/uri'; -import { Event, Disposable, TextDocumentContentChangeDelta, Reference } from '@theia/core/lib/common'; +import { Event, Disposable, TextDocumentContentChangeDelta, Reference, Is } from '@theia/core/lib/common'; import { Saveable, Navigatable, Widget } from '@theia/core/lib/browser'; import { EditorDecoration } from './decorations'; @@ -331,8 +331,8 @@ export interface ReplaceOperation { } export namespace TextEditorSelection { - export function is(e: unknown): e is TextEditorSelection { - return !!e && typeof e === 'object' && (e as TextEditorSelection).uri instanceof URI; + export function is(arg: unknown): arg is TextEditorSelection { + return Is.object(arg) && arg.uri instanceof URI; } } diff --git a/packages/filesystem/src/browser/file-selection.ts b/packages/filesystem/src/browser/file-selection.ts index 81aefa5942992..9acedd810c259 100644 --- a/packages/filesystem/src/browser/file-selection.ts +++ b/packages/filesystem/src/browser/file-selection.ts @@ -16,6 +16,7 @@ import { SelectionService } from '@theia/core/lib/common/selection-service'; import { SelectionCommandHandler } from '@theia/core/lib/common/selection-command-handler'; +import { Is } from '@theia/core/lib/common/is'; import { FileStat } from '../common/files'; export interface FileSelection { @@ -23,7 +24,7 @@ export interface FileSelection { } export namespace FileSelection { export function is(arg: unknown): arg is FileSelection { - return !!arg && typeof arg === 'object' && ('fileStat' in arg) && FileStat.is((arg as FileSelection).fileStat); + return Is.object(arg) && FileStat.is(arg.fileStat); } export class CommandHandler extends SelectionCommandHandler { diff --git a/packages/filesystem/src/browser/file-tree/file-tree.ts b/packages/filesystem/src/browser/file-tree/file-tree.ts index 733ec2766e9c4..b933ad7c3d1a3 100644 --- a/packages/filesystem/src/browser/file-tree/file-tree.ts +++ b/packages/filesystem/src/browser/file-tree/file-tree.ts @@ -16,6 +16,7 @@ import { injectable, inject } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; +import { Is } from '@theia/core/lib/common/is'; import { TreeNode, CompositeTreeNode, SelectableTreeNode, ExpandableTreeNode, TreeImpl } from '@theia/core/lib/browser'; import { Mutable } from '@theia/core/lib/common/types'; import { FileStat, Stat, FileType, FileOperationError, FileOperationResult } from '../../common/files'; @@ -102,7 +103,7 @@ export interface FileStatNode extends SelectableTreeNode, Mutable, } export namespace FileStatNode { export function is(node: unknown): node is FileStatNode { - return !!node && typeof node === 'object' && 'fileStat' in node; + return Is.object(node) && 'fileStat' in node; } export function getUri(node: TreeNode | undefined): string | undefined { @@ -120,7 +121,7 @@ export type FileStatNodeData = Omit & { }; export namespace FileStatNodeData { export function is(node: unknown): node is FileStatNodeData { - return !!node && typeof node === 'object' && 'uri' in node && ('fileStat' in node || 'stat' in node); + return Is.object(node) && 'uri' in node && ('fileStat' in node || 'stat' in node); } } diff --git a/packages/filesystem/src/common/download/file-download-data.ts b/packages/filesystem/src/common/download/file-download-data.ts index 048e8da47bce9..b47d14b52ff09 100644 --- a/packages/filesystem/src/common/download/file-download-data.ts +++ b/packages/filesystem/src/common/download/file-download-data.ts @@ -14,12 +14,14 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Is } from '@theia/core/lib/common/is'; + export interface FileDownloadData { readonly uris: string[]; } export namespace FileDownloadData { export function is(arg: unknown): arg is FileDownloadData { - return !!arg && typeof arg === 'object' && 'uris' in arg; + return Is.object(arg) && 'uris' in arg; } } diff --git a/packages/filesystem/src/common/files.ts b/packages/filesystem/src/common/files.ts index 2dfed8108bb9c..f41ffef0240d4 100644 --- a/packages/filesystem/src/common/files.ts +++ b/packages/filesystem/src/common/files.ts @@ -26,6 +26,7 @@ import { BinaryBuffer, BinaryBufferReadableStream } from '@theia/core/lib/common import type { TextDocumentContentChangeEvent } from '@theia/core/shared/vscode-languageserver-protocol'; import { ReadableStreamEvents } from '@theia/core/lib/common/stream'; import { CancellationToken } from '@theia/core/lib/common/cancellation'; +import { Is } from '@theia/core/lib/common/is'; export const enum FileOperation { CREATE, @@ -201,9 +202,9 @@ export interface BaseStat { } export namespace BaseStat { export function is(arg: unknown): arg is BaseStat { - return !!arg && typeof arg === 'object' - && ('resource' in arg && (arg as BaseStat).resource instanceof URI) - && ('name' in arg && typeof (arg as BaseStat).name === 'string'); + return Is.object(arg) + && arg.resource instanceof URI + && typeof arg.name === 'string'; } } diff --git a/packages/git/src/common/git-model.ts b/packages/git/src/common/git-model.ts index 894a5488f978e..d23c5ad10385a 100644 --- a/packages/git/src/common/git-model.ts +++ b/packages/git/src/common/git-model.ts @@ -15,8 +15,7 @@ // ***************************************************************************** import URI from '@theia/core/lib/common/uri'; -import { Path } from '@theia/core'; -import { nls } from '@theia/core/lib/common/nls'; +import { Is, Path, nls } from '@theia/core'; export interface WorkingDirectoryStatus { @@ -213,7 +212,7 @@ export namespace Repository { return repository === repository2; } export function is(repository: unknown): repository is Repository { - return !!repository && typeof repository === 'object' && 'localUri' in repository; + return Is.object(repository) && 'localUri' in repository; } export function relativePath(repository: Repository | URI, uri: URI | string): Path | undefined { const repositoryUri = new URI(Repository.is(repository) ? repository.localUri : String(repository)); diff --git a/packages/git/src/common/git-watcher.ts b/packages/git/src/common/git-watcher.ts index a078e376f3cb3..4f511490bf941 100644 --- a/packages/git/src/common/git-watcher.ts +++ b/packages/git/src/common/git-watcher.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { injectable, inject } from '@theia/core/shared/inversify'; -import { JsonRpcServer, JsonRpcProxy } from '@theia/core'; +import { JsonRpcServer, JsonRpcProxy, Is } from '@theia/core'; import { Repository, WorkingDirectoryStatus } from './git-model'; import { Disposable, DisposableCollection, Emitter, Event } from '@theia/core/lib/common'; @@ -48,7 +48,7 @@ export namespace GitStatusChangeEvent { * @param event the argument to check whether it is a Git status change event or not. */ export function is(event: unknown): event is GitStatusChangeEvent { - return !!event && typeof event === 'object' && ('source' in event) && ('status' in event); + return Is.object(event) && ('source' in event) && ('status' in event); } } diff --git a/packages/git/src/common/git.ts b/packages/git/src/common/git.ts index 9ed52916f2b35..996977efd878a 100644 --- a/packages/git/src/common/git.ts +++ b/packages/git/src/common/git.ts @@ -15,7 +15,7 @@ // ***************************************************************************** import { ChildProcess } from 'child_process'; -import { Disposable } from '@theia/core'; +import { Disposable, Is } from '@theia/core'; import { Repository, WorkingDirectoryStatus, Branch, GitResult, GitError, GitFileStatus, GitFileChange, CommitWithChanges, GitFileBlame, Remote as RemoteModel, StashEntry @@ -854,42 +854,42 @@ export namespace GitUtils { * `true` if the argument is an option for renaming an existing branch in the repository. */ export function isBranchRename(arg: unknown): arg is Git.Options.BranchCommand.Rename { - return !!arg && typeof arg === 'object' && ('newName' in arg); + return Is.object(arg) && 'newName' in arg; } /** * `true` if the argument is an option for deleting an existing branch in the repository. */ export function isBranchDelete(arg: unknown): arg is Git.Options.BranchCommand.Delete { - return !!arg && typeof arg === 'object' && ('toDelete' in arg); + return Is.object(arg) && 'toDelete' in arg; } /** * `true` if the argument is an option for creating a new branch in the repository. */ export function isBranchCreate(arg: unknown): arg is Git.Options.BranchCommand.Create { - return !!arg && typeof arg === 'object' && ('toCreate' in arg); + return Is.object(arg) && 'toCreate' in arg; } /** * `true` if the argument is an option for listing the branches in a repository. */ export function isBranchList(arg: unknown): arg is Git.Options.BranchCommand.List { - return !!arg && typeof arg === 'object' && ('type' in arg); + return Is.object(arg) && 'type' in arg; } /** * `true` if the argument is an option for checking out a new local branch. */ export function isBranchCheckout(arg: unknown): arg is Git.Options.Checkout.CheckoutBranch { - return !!arg && typeof arg === 'object' && ('branch' in arg); + return Is.object(arg) && 'branch' in arg; } /** * `true` if the argument is an option for checking out a working tree file. */ export function isWorkingTreeFileCheckout(arg: unknown): arg is Git.Options.Checkout.WorkingTreeFile { - return !!arg && typeof arg === 'object' && ('paths' in arg); + return Is.object(arg) && 'paths' in arg; } /** diff --git a/packages/markers/src/browser/problem/problem-selection.ts b/packages/markers/src/browser/problem/problem-selection.ts index 39552b170de0a..f2e74eda1981e 100644 --- a/packages/markers/src/browser/problem/problem-selection.ts +++ b/packages/markers/src/browser/problem/problem-selection.ts @@ -16,6 +16,7 @@ import { SelectionService } from '@theia/core/lib/common/selection-service'; import { SelectionCommandHandler } from '@theia/core/lib/common/selection-command-handler'; +import { Is } from '@theia/core/lib/common/is'; import { Marker } from '../../common/marker'; import { ProblemMarker } from '../../common/problem-marker'; @@ -24,7 +25,7 @@ export interface ProblemSelection { } export namespace ProblemSelection { export function is(arg: unknown): arg is ProblemSelection { - return !!arg && typeof arg === 'object' && ('marker' in arg) && ProblemMarker.is((arg as ProblemSelection).marker); + return Is.object(arg) && ProblemMarker.is(arg.marker); } export class CommandHandler extends SelectionCommandHandler { diff --git a/packages/monaco/src/browser/monaco-indexed-db.ts b/packages/monaco/src/browser/monaco-indexed-db.ts index 2e29b183b10f4..04b76e64ecb0f 100644 --- a/packages/monaco/src/browser/monaco-indexed-db.ts +++ b/packages/monaco/src/browser/monaco-indexed-db.ts @@ -21,7 +21,7 @@ import * as monaco from '@theia/monaco-editor-core'; import { injectable } from '@theia/core/shared/inversify'; import type { ThemeMix } from './textmate/monaco-theme-types'; import { Theme } from '@theia/core/lib/common/theme'; -import { Emitter, Event } from '@theia/core'; +import { Emitter, Event, Is } from '@theia/core'; let _monacoDB: Promise | undefined; if ('indexedDB' in window) { @@ -45,7 +45,7 @@ export interface MonacoThemeState { } export namespace MonacoThemeState { export function is(state: unknown): state is MonacoThemeState { - return !!state && typeof state === 'object' && 'id' in state && 'label' in state && 'uiTheme' in state && 'data' in state; + return Is.object(state) && 'id' in state && 'label' in state && 'uiTheme' in state && 'data' in state; } } diff --git a/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts b/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts index 6215a39aebaa7..f471521c51cef 100644 --- a/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts +++ b/packages/monaco/src/browser/monaco-snippet-suggest-provider.ts @@ -26,6 +26,7 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileOperationError } from '@theia/filesystem/lib/common/files'; import * as monaco from '@theia/monaco-editor-core'; import { SnippetParser } from '@theia/monaco-editor-core/esm/vs/editor/contrib/snippet/browser/snippetParser'; +import { Is } from '@theia/core/lib/common/is'; @injectable() export class MonacoSnippetSuggestProvider implements monaco.languages.CompletionItemProvider { @@ -191,15 +192,11 @@ export class MonacoSnippetSuggestProvider implements monaco.languages.Completion return toDispose; } protected parseSnippets(snippets: JsonSerializedSnippets | undefined, accept: (name: string, snippet: JsonSerializedSnippet) => void): void { - if (typeof snippets === 'object') { - // eslint-disable-next-line guard-for-in - for (const name in snippets) { - const scopeOrTemplate = snippets[name]; - if (JsonSerializedSnippet.is(scopeOrTemplate)) { - accept(name, scopeOrTemplate); - } else { - this.parseSnippets(scopeOrTemplate, accept); - } + for (const [name, scopeOrTemplate] of Object.entries(snippets ?? {})) { + if (JsonSerializedSnippet.is(scopeOrTemplate)) { + accept(name, scopeOrTemplate); + } else { + this.parseSnippets(scopeOrTemplate, accept); } } } @@ -250,7 +247,7 @@ export interface JsonSerializedSnippet { } export namespace JsonSerializedSnippet { export function is(obj: unknown): obj is JsonSerializedSnippet { - return !!obj && typeof obj === 'object' && 'body' in obj && 'prefix' in obj; + return Is.object(obj) && 'body' in obj && 'prefix' in obj; } } diff --git a/packages/monaco/src/browser/monaco-workspace.ts b/packages/monaco/src/browser/monaco-workspace.ts index 8e785d61a49d2..fde4a258bf668 100644 --- a/packages/monaco/src/browser/monaco-workspace.ts +++ b/packages/monaco/src/browser/monaco-workspace.ts @@ -37,6 +37,7 @@ import { import { IEditorWorkerService } from '@theia/monaco-editor-core/esm/vs/editor/common/services/editorWorker'; import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices'; import { EndOfLineSequence } from '@theia/monaco-editor-core/esm/vs/editor/common/model'; +import { Is } from '@theia/core/lib/common/is'; export namespace WorkspaceFileEdit { export function is(arg: Edit): arg is monaco.languages.WorkspaceFileEdit { @@ -47,12 +48,9 @@ export namespace WorkspaceFileEdit { export namespace WorkspaceTextEdit { export function is(arg: Edit): arg is monaco.languages.WorkspaceTextEdit { - return !!arg && typeof arg === 'object' - && 'resource' in arg + return Is.object(arg) && monaco.Uri.isUri(arg.resource) - && 'edit' in arg - && arg.edit !== null - && typeof arg.edit === 'object'; + && Is.object(arg.edit); } } @@ -60,8 +58,7 @@ export type Edit = monaco.languages.WorkspaceFileEdit | monaco.languages.Workspa export namespace ResourceFileEdit { export function is(arg: ResourceEdit): arg is MonacoResourceFileEdit { - return typeof arg === 'object' && (('oldResource' in arg) && monaco.Uri.isUri((arg as MonacoResourceFileEdit).oldResource)) || - ('newResource' in arg && monaco.Uri.isUri((arg as MonacoResourceFileEdit).newResource)); + return Is.object(arg) && (monaco.Uri.isUri(arg.oldResource) || monaco.Uri.isUri(arg.newResource)); } } diff --git a/packages/outline-view/src/browser/outline-view-widget.tsx b/packages/outline-view/src/browser/outline-view-widget.tsx index f3b6561a90ae8..d197046148ab4 100644 --- a/packages/outline-view/src/browser/outline-view-widget.tsx +++ b/packages/outline-view/src/browser/outline-view-widget.tsx @@ -29,7 +29,7 @@ import { } from '@theia/core/lib/browser'; import { OutlineViewTreeModel } from './outline-view-tree-model'; import { Message } from '@theia/core/shared/@phosphor/messaging'; -import { Emitter, Mutable, UriSelection } from '@theia/core'; +import { Emitter, Is, Mutable, UriSelection } from '@theia/core'; import * as React from '@theia/core/shared/react'; import { Range } from '@theia/core/shared/vscode-languageserver-protocol'; import URI from '@theia/core/lib/common/uri'; @@ -64,7 +64,7 @@ export namespace OutlineSymbolInformationNode { } export function hasRange(node: unknown): node is { range: Range } { - return typeof node === 'object' && !!node && 'range' in node && Range.is((node as { range: Range }).range); + return Is.object<{ range: Range }>(node) && Range.is(node.range); } } diff --git a/packages/plugin-ext/src/common/plugin-api-rpc.ts b/packages/plugin-ext/src/common/plugin-api-rpc.ts index aaaf79f10c844..17fbae988650e 100644 --- a/packages/plugin-ext/src/common/plugin-api-rpc.ts +++ b/packages/plugin-ext/src/common/plugin-api-rpc.ts @@ -107,7 +107,7 @@ import type { import { SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/common/base-terminal-protocol'; import { ThemeType } from '@theia/core/lib/common/theme'; import { Disposable } from '@theia/core/lib/common/disposable'; -import { PickOptions, QuickInputButtonHandle } from '@theia/core/lib/common'; +import { Is, PickOptions, QuickInputButtonHandle } from '@theia/core/lib/common'; import { Severity } from '@theia/core/lib/common/severity'; export interface PreferenceData { @@ -776,7 +776,7 @@ export interface TreeViewSelection { } export namespace TreeViewSelection { export function is(arg: unknown): arg is TreeViewSelection { - return !!arg && typeof arg === 'object' && 'treeViewId' in arg && 'treeItemId' in arg; + return Is.object(arg) && 'treeViewId' in arg && 'treeItemId' in arg; } } @@ -822,7 +822,7 @@ export interface ScmCommandArg { } export namespace ScmCommandArg { export function is(arg: unknown): arg is ScmCommandArg { - return !!arg && typeof arg === 'object' && 'sourceControlHandle' in arg; + return Is.object(arg) && 'sourceControlHandle' in arg; } } @@ -838,7 +838,7 @@ export interface ScmExt { export namespace TimelineCommandArg { export function is(arg: unknown): arg is TimelineCommandArg { - return !!arg && typeof arg === 'object' && 'timelineHandle' in arg; + return Is.object(arg) && 'timelineHandle' in arg; } } export interface TimelineCommandArg { @@ -857,7 +857,7 @@ export interface DecorationReply { [id: number]: DecorationData; } export namespace CommentsCommandArg { export function is(arg: unknown): arg is CommentsCommandArg { - return !!arg && typeof arg === 'object' && 'commentControlHandle' in arg && 'commentThreadHandle' in arg && 'text' in arg && !('commentUniqueId' in arg); + return Is.object(arg) && 'commentControlHandle' in arg && 'commentThreadHandle' in arg && 'text' in arg && !('commentUniqueId' in arg); } } export interface CommentsCommandArg { @@ -868,7 +868,7 @@ export interface CommentsCommandArg { export namespace CommentsContextCommandArg { export function is(arg: unknown): arg is CommentsContextCommandArg { - return !!arg && typeof arg === 'object' && 'commentControlHandle' in arg && 'commentThreadHandle' in arg && 'commentUniqueId' in arg && !('text' in arg); + return Is.object(arg) && 'commentControlHandle' in arg && 'commentThreadHandle' in arg && 'commentUniqueId' in arg && !('text' in arg); } } export interface CommentsContextCommandArg { @@ -879,7 +879,7 @@ export interface CommentsContextCommandArg { export namespace CommentsEditCommandArg { export function is(arg: unknown): arg is CommentsEditCommandArg { - return !!arg && typeof arg === 'object' && 'commentControlHandle' in arg && 'commentThreadHandle' in arg && 'commentUniqueId' in arg && 'text' in arg; + return Is.object(arg) && 'commentControlHandle' in arg && 'commentThreadHandle' in arg && 'commentUniqueId' in arg && 'text' in arg; } } export interface CommentsEditCommandArg { diff --git a/packages/plugin-ext/src/common/rpc-protocol.ts b/packages/plugin-ext/src/common/rpc-protocol.ts index efd5b9503bee3..b9c88e5a91397 100644 --- a/packages/plugin-ext/src/common/rpc-protocol.ts +++ b/packages/plugin-ext/src/common/rpc-protocol.ts @@ -30,6 +30,7 @@ import URI from '@theia/core/lib/common/uri'; import { CancellationToken, CancellationTokenSource } from '@theia/core/shared/vscode-languageserver-protocol'; import { Range, Position } from '../plugin/types-impl'; import { BinaryBuffer } from '@theia/core/lib/common/buffer'; +import { Is } from '@theia/core/lib/common/is'; export interface MessageConnection { send(msg: string): void; @@ -71,7 +72,7 @@ export namespace ConnectionClosedError { return Object.assign(new Error(message), { code }); } export function is(error: unknown): error is ConnectionClosedError { - return !!error && typeof error === 'object' && 'code' in error && (error as ConnectionClosedError).code === code; + return Is.object(error) && 'code' in error && (error as ConnectionClosedError).code === code; } } @@ -469,8 +470,7 @@ enum SerializedObjectType { } function isSerializedObject(obj: unknown): obj is SerializedObject { - const serializedObject = obj as SerializedObject; - return !!obj && typeof obj === 'object' && serializedObject.$type !== undefined && serializedObject.data !== undefined; + return Is.object(obj) && obj.$type !== undefined && obj.data !== undefined; } export const enum MessageType { diff --git a/packages/plugin-ext/src/common/types.ts b/packages/plugin-ext/src/common/types.ts index d1c8b4c6722f7..20940d637e93b 100644 --- a/packages/plugin-ext/src/common/types.ts +++ b/packages/plugin-ext/src/common/types.ts @@ -19,13 +19,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Is } from '@theia/core/lib/common/is'; + /** * Returns `true` if the parameter has type "object" and not null, an array, a regexp, a date. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function isObject(obj: any): boolean { - return typeof obj === 'object' - && obj !== null // eslint-disable-line @typescript-eslint/no-explicit-any +export function isObject(obj: unknown): boolean { + return Is.object(obj) && !Array.isArray(obj) && !(obj instanceof RegExp) && !(obj instanceof Date); diff --git a/packages/plugin-ext/src/hosted/node/hosted-plugin-localization-service.ts b/packages/plugin-ext/src/hosted/node/hosted-plugin-localization-service.ts index a3761bdefada3..8531a31a2f05e 100644 --- a/packages/plugin-ext/src/hosted/node/hosted-plugin-localization-service.ts +++ b/packages/plugin-ext/src/hosted/node/hosted-plugin-localization-service.ts @@ -23,7 +23,7 @@ import { DeployedPlugin, Localization as PluginLocalization, PluginIdentifiers } import { URI } from '@theia/core/shared/vscode-uri'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { BackendApplicationContribution } from '@theia/core/lib/node'; -import { Disposable } from '@theia/core'; +import { Disposable, Is } from '@theia/core'; import { Deferred } from '@theia/core/lib/common/promise-util'; export interface VSCodeNlsConfig { @@ -205,7 +205,7 @@ interface LocalizeInfo { } function isLocalizeInfo(obj: unknown): obj is LocalizeInfo { - return typeof obj === 'object' && obj && 'message' in obj || false; + return Is.object(obj) && 'message' in obj || false; } function coerceLocalizations(translations: Record): Record { @@ -243,7 +243,7 @@ function localizePackage(value: unknown, translations: PackageTranslation, callb } return result; } - if (typeof value === 'object' && value) { + if (Is.object(value)) { const result: Record = {}; for (const [name, item] of Object.entries(value)) { result[name] = localizePackage(item, translations, callback); diff --git a/packages/plugin-ext/src/hosted/node/plugin-host.ts b/packages/plugin-ext/src/hosted/node/plugin-host.ts index fa54f21beb537..645921efe34d1 100644 --- a/packages/plugin-ext/src/hosted/node/plugin-host.ts +++ b/packages/plugin-ext/src/hosted/node/plugin-host.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import '@theia/core/shared/reflect-metadata'; import { Emitter } from '@theia/core/lib/common/event'; import { RPCProtocolImpl, MessageType, ConnectionClosedError } from '../../common/rpc-protocol'; import { PluginHostRPC } from './plugin-host-rpc'; @@ -82,8 +83,7 @@ const rpc = new RPCProtocolImpl({ process.send(m); } } -}, -{ +}, { reviver: reviver }); diff --git a/packages/plugin-ext/src/main/browser/view/tree-view-decorator-service.ts b/packages/plugin-ext/src/main/browser/view/tree-view-decorator-service.ts index c6df32ad46596..e3840d8277341 100644 --- a/packages/plugin-ext/src/main/browser/view/tree-view-decorator-service.ts +++ b/packages/plugin-ext/src/main/browser/view/tree-view-decorator-service.ts @@ -16,9 +16,9 @@ import { inject, injectable, interfaces, named } from '@theia/core/shared/inversify'; import { AbstractTreeDecoratorService, TreeDecorator } from '@theia/core/lib/browser/tree/tree-decorator'; -import { bindContributionProvider, ContributionProvider } from '@theia/core'; +import { bindContributionProvider, ContributionProvider, Is } from '@theia/core'; import { TreeNode } from '@theia/core/lib/browser'; -import { TreeItem, Uri } from '@theia/plugin'; +import { TreeItem } from '@theia/plugin'; import URI from '@theia/core/lib/common/uri'; import { FileTreeDecoratorAdapter } from '@theia/filesystem/lib/browser'; @@ -32,9 +32,8 @@ export class TreeViewDecoratorAdapter extends FileTreeDecoratorAdapter { } } - protected isTreeItem(node: unknown): node is TreeItem & { resourceUri: Uri } { - const candidate = node as TreeItem; - return !!candidate && typeof node === 'object' && 'resourceUri' in candidate && !!candidate.resourceUri; + protected isTreeItem(node: unknown): node is TreeItem { + return Is.object(node) && !!node.resourceUri; } } diff --git a/packages/plugin-ext/src/plugin/languages/code-action.ts b/packages/plugin-ext/src/plugin/languages/code-action.ts index 5a4729d29cf4e..4ef2a67a25c2b 100644 --- a/packages/plugin-ext/src/plugin/languages/code-action.ts +++ b/packages/plugin-ext/src/plugin/languages/code-action.ts @@ -24,6 +24,7 @@ import { Diagnostics } from './diagnostics'; import { CodeActionKind } from '../types-impl'; import { CommandRegistryImpl } from '../command-registry'; import { DisposableCollection } from '@theia/core/lib/common/disposable'; +import { Is } from '@theia/core/lib/common/is'; export class CodeActionAdapter { @@ -147,16 +148,15 @@ export class CodeActionAdapter { } private static _isCommand(arg: unknown): arg is theia.Command { - return !!arg && typeof arg === 'object' && typeof (arg as theia.Command).command === 'string'; + return Is.object(arg) && typeof arg.command === 'string'; } - private static _isSelection(obj: unknown): obj is Selection { - const selection = obj as Selection; - return !!obj && typeof obj === 'object' - && typeof selection.selectionStartLineNumber === 'number' - && typeof selection.selectionStartColumn === 'number' - && typeof selection.positionLineNumber === 'number' - && typeof selection.positionColumn === 'number'; + private static _isSelection(arg: unknown): arg is Selection { + return Is.object(arg) + && typeof arg.selectionStartLineNumber === 'number' + && typeof arg.selectionStartColumn === 'number' + && typeof arg.positionLineNumber === 'number' + && typeof arg.positionColumn === 'number'; } } diff --git a/packages/plugin-ext/src/plugin/type-converters.ts b/packages/plugin-ext/src/plugin/type-converters.ts index 9126224e0df44..6105638a7a84e 100644 --- a/packages/plugin-ext/src/plugin/type-converters.ts +++ b/packages/plugin-ext/src/plugin/type-converters.ts @@ -28,6 +28,7 @@ import * as types from './types-impl'; import { UriComponents } from '../common/uri-components'; import { isReadonlyArray } from '../common/arrays'; import { MarkdownString as MarkdownStringDTO } from '@theia/core/lib/common/markdown-rendering'; +import { Is } from '@theia/core/lib/common/is'; const SIDE_GROUP = -2; const ACTIVE_GROUP = -1; @@ -138,7 +139,7 @@ export function toPosition(position: Position): types.Position { } function isDecorationOptions(arg: unknown): arg is theia.DecorationOptions { - return !!arg && typeof arg === 'object' && typeof (arg as theia.DecorationOptions).range !== 'undefined'; + return Is.object(arg) && typeof arg.range !== 'undefined'; } export function isDecorationOptionsArr(something: theia.Range[] | theia.DecorationOptions[]): something is theia.DecorationOptions[] { @@ -182,9 +183,9 @@ interface Codeblock { } function isCodeblock(arg: unknown): arg is Codeblock { - return !!arg && typeof arg === 'object' - && typeof (arg as Codeblock).language === 'string' - && typeof (arg as Codeblock).value === 'string'; + return Is.object(arg) + && typeof arg.language === 'string' + && typeof arg.value === 'string'; } export function fromMarkdown(markup: theia.MarkdownString | theia.MarkedString): MarkdownStringDTO { @@ -667,57 +668,47 @@ export function toSymbolTag(kind: model.SymbolTag): types.SymbolTag { } export function isModelLocation(arg: unknown): arg is model.Location { - if (!arg) { - return false; - } - return !!arg && - typeof arg === 'object' && - isModelRange((arg as model.Location).range) && - isUriComponents((arg as model.Location).uri); + return Is.object(arg) && + isModelRange(arg.range) && + isUriComponents(arg.uri); } export function isModelRange(arg: unknown): arg is model.Range { - const range = arg as model.Range; - return !!arg && typeof arg === 'object' && - typeof range.startLineNumber === 'number' && - typeof range.startColumn === 'number' && - typeof range.endLineNumber === 'number' && - typeof range.endColumn === 'number'; + return Is.object(arg) && + typeof arg.startLineNumber === 'number' && + typeof arg.startColumn === 'number' && + typeof arg.endLineNumber === 'number' && + typeof arg.endColumn === 'number'; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isUriComponents(arg: unknown): arg is UriComponents { - const uriComponents = arg as UriComponents; - return !!arg && typeof arg === 'object' && - typeof uriComponents.scheme === 'string' && - typeof uriComponents.path === 'string' && - typeof uriComponents.query === 'string' && - typeof uriComponents.fragment === 'string'; + return Is.object(arg) && + typeof arg.scheme === 'string' && + typeof arg.path === 'string' && + typeof arg.query === 'string' && + typeof arg.fragment === 'string'; } export function isModelCallHierarchyItem(arg: unknown): arg is model.CallHierarchyItem { - const item = arg as model.CallHierarchyItem; - return !!item && typeof item === 'object' - && isModelRange(item.range) - && isModelRange(item.selectionRange) - && isUriComponents(item.uri) - && !!item.name; + return Is.object(arg) + && isModelRange(arg.range) + && isModelRange(arg.selectionRange) + && isUriComponents(arg.uri) + && !!arg.name; } export function isModelCallHierarchyIncomingCall(arg: unknown): arg is model.CallHierarchyIncomingCall { - const maybeIncomingCall = arg as model.CallHierarchyIncomingCall; - return !!arg && typeof arg === 'object' && - 'from' in maybeIncomingCall && - 'fromRanges' in maybeIncomingCall && - isModelCallHierarchyItem(maybeIncomingCall.from); + return Is.object(arg) && + 'from' in arg && + 'fromRanges' in arg && + isModelCallHierarchyItem(arg.from); } export function isModelCallHierarchyOutgoingCall(arg: unknown): arg is model.CallHierarchyOutgoingCall { - const maybeOutgoingCall = arg as model.CallHierarchyOutgoingCall; - return !!arg && typeof arg === 'object' && - 'to' in maybeOutgoingCall && - 'fromRanges' in maybeOutgoingCall && - isModelCallHierarchyItem(maybeOutgoingCall.to); + return Is.object(arg) && + 'to' in arg && + 'fromRanges' in arg && + isModelCallHierarchyItem(arg.to); } export function toLocation(value: model.Location): types.Location { @@ -771,12 +762,11 @@ export function toCallHierarchyOutgoingCall(value: model.CallHierarchyOutgoingCa } export function isModelTypeHierarchyItem(arg: unknown): arg is model.TypeHierarchyItem { - const item = arg as model.TypeHierarchyItem; - return !!item && typeof item === 'object' - && isModelRange(item.range) - && isModelRange(item.selectionRange) - && isUriComponents(item.uri) - && !!item.name; + return Is.object(arg) + && isModelRange(arg.range) + && isModelRange(arg.selectionRange) + && isUriComponents(arg.uri) + && !!arg.name; } export function fromTypeHierarchyItem(item: types.TypeHierarchyItem): model.TypeHierarchyItem { diff --git a/packages/plugin-ext/src/plugin/types-impl.ts b/packages/plugin-ext/src/plugin/types-impl.ts index b990a737b52e4..b9eb511b987d9 100644 --- a/packages/plugin-ext/src/plugin/types-impl.ts +++ b/packages/plugin-ext/src/plugin/types-impl.ts @@ -32,6 +32,7 @@ import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from '@the import * as paths from 'path'; import { ObjectsTransferrer } from '../common/rpc-protocol'; import { es5ClassCompat } from '../common/types'; +import { Is } from '@theia/core/lib/common/is'; /** * A reviver that takes URI's transferred via JSON.stringify() and makes @@ -546,14 +547,13 @@ export class Range { return new Range(start, end); } - static isRange(thing: unknown): thing is theia.Range { - if (thing instanceof Range) { + static isRange(arg: unknown): arg is theia.Range { + if (arg instanceof Range) { return true; } - const range = thing as theia.Range; - return !!thing && typeof thing === 'object' - && Position.isPosition(range.start) - && Position.isPosition(range.end); + return Is.object(arg) + && Position.isPosition(arg.start) + && Position.isPosition(arg.end); } toJSON(): unknown { @@ -736,7 +736,7 @@ export class ThemeIcon { export namespace ThemeIcon { export function is(item: unknown): item is ThemeIcon { - return typeof item === 'object' && !!item && 'id' in item; + return Is.object(item) && 'id' in item; } } @@ -2784,7 +2784,7 @@ export class SemanticTokensLegend { } function isStrArrayOrUndefined(arg: unknown): arg is string[] | undefined { - return ((typeof arg === 'undefined') || (Array.isArray(arg) && arg.every(e => typeof e === 'string'))); + return typeof arg === 'undefined' || Is.stringArray(arg); } @es5ClassCompat diff --git a/packages/preferences/src/browser/views/components/preference-file-input.ts b/packages/preferences/src/browser/views/components/preference-file-input.ts index 4510a1cc26731..19a0cb90fb977 100644 --- a/packages/preferences/src/browser/views/components/preference-file-input.ts +++ b/packages/preferences/src/browser/views/components/preference-file-input.ts @@ -14,6 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** +import { Is } from '@theia/core/lib/common/is'; import { nls } from '@theia/core/lib/common/nls'; import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; import { OpenFileDialogProps } from '@theia/filesystem/lib/browser'; @@ -25,13 +26,13 @@ import { PreferenceLeafNodeRendererContribution } from './preference-node-render import { PreferenceStringInputRenderer } from './preference-string-input'; export interface FileNodeTypeDetails { - isFilepath: boolean; + isFilepath: true; selectionProps?: Partial; } export namespace FileNodeTypeDetails { - export function is(typeDetails?: unknown): typeDetails is FileNodeTypeDetails { - return !!typeDetails && typeof typeDetails === 'object' && !!(typeDetails as FileNodeTypeDetails).isFilepath; + export function is(typeDetails: unknown): typeDetails is FileNodeTypeDetails { + return Is.object(typeDetails) && !!typeDetails.isFilepath; } } diff --git a/packages/preview/src/browser/preview-handler.ts b/packages/preview/src/browser/preview-handler.ts index f415882e7ee0c..d02a40db7666c 100644 --- a/packages/preview/src/browser/preview-handler.ts +++ b/packages/preview/src/browser/preview-handler.ts @@ -16,7 +16,7 @@ import { inject, injectable, named } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; -import { ContributionProvider, MaybePromise, Prioritizeable } from '@theia/core'; +import { ContributionProvider, Is, MaybePromise, Prioritizeable } from '@theia/core'; export const PreviewHandler = Symbol('PreviewHandler'); @@ -36,7 +36,7 @@ export interface RenderContentParams { export namespace RenderContentParams { export function is(params: unknown): params is RenderContentParams { - return !!params && typeof params === 'object' && 'content' in params && 'originUri' in params; + return Is.object(params) && 'content' in params && 'originUri' in params; } } diff --git a/packages/process/src/node/process.ts b/packages/process/src/node/process.ts index a234d11864e77..c1cf3db29736a 100644 --- a/packages/process/src/node/process.ts +++ b/packages/process/src/node/process.ts @@ -16,7 +16,7 @@ import { injectable, unmanaged } from '@theia/core/shared/inversify'; import { ProcessManager } from './process-manager'; -import { ILogger, Emitter, Event } from '@theia/core/lib/common'; +import { ILogger, Emitter, Event, Is } from '@theia/core/lib/common'; import { FileUri } from '@theia/core/lib/node'; import { isOSX, isWindows } from '@theia/core'; import { Readable, Writable } from 'stream'; @@ -193,7 +193,7 @@ export abstract class Process { } protected isForkOptions(options: unknown): options is ForkOptions { - return !!options && typeof options === 'object' && !!(options as ForkOptions).modulePath; + return Is.object(options) && !!options.modulePath; } protected readonly initialCwd: string; diff --git a/packages/scm-extra/src/browser/history/scm-history-widget.tsx b/packages/scm-extra/src/browser/history/scm-history-widget.tsx index 6bae5883a8218..0df75af4a0c71 100644 --- a/packages/scm-extra/src/browser/history/scm-history-widget.tsx +++ b/packages/scm-extra/src/browser/history/scm-history-widget.tsx @@ -15,7 +15,7 @@ // ***************************************************************************** import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; -import { Event as TheiaEvent, DisposableCollection } from '@theia/core'; +import { Event as TheiaEvent, DisposableCollection, Is } from '@theia/core'; import { OpenerService, open, StatefulWidget, SELECTED_CLASS, WidgetManager, ApplicationShell, codicon } from '@theia/core/lib/browser'; import { CancellationTokenSource } from '@theia/core/lib/common/cancellation'; import { Message } from '@theia/core/shared/@phosphor/messaging'; @@ -48,7 +48,7 @@ export interface ScmCommitNode { export namespace ScmCommitNode { export function is(node: unknown): node is ScmCommitNode { - return !!node && typeof node === 'object' && 'commitDetails' in node && 'expanded' in node && 'selected' in node; + return Is.object(node) && 'commitDetails' in node && 'expanded' in node && 'selected' in node; } } diff --git a/packages/scm-extra/src/browser/scm-file-change-node.ts b/packages/scm-extra/src/browser/scm-file-change-node.ts index a42d7b393e0aa..f9607fc261ca0 100644 --- a/packages/scm-extra/src/browser/scm-file-change-node.ts +++ b/packages/scm-extra/src/browser/scm-file-change-node.ts @@ -16,6 +16,7 @@ import { ScmCommit } from '@theia/scm/lib/browser/scm-provider'; import URI from '@theia/core/lib/common/uri'; +import { Is } from '@theia/core/lib/common/is'; export interface ScmFileChangeNode { readonly fileChange: ScmFileChange; @@ -24,7 +25,7 @@ export interface ScmFileChangeNode { } export namespace ScmFileChangeNode { export function is(node: unknown): node is ScmFileChangeNode { - return !!node && typeof node === 'object' && 'fileChange' in node && 'commitId' in node; + return Is.object(node) && 'fileChange' in node && 'commitId' in node; } } diff --git a/packages/task/src/browser/task-configuration-model.ts b/packages/task/src/browser/task-configuration-model.ts index e9814fb3080df..f63e5b23e2c12 100644 --- a/packages/task/src/browser/task-configuration-model.ts +++ b/packages/task/src/browser/task-configuration-model.ts @@ -19,6 +19,7 @@ import { Emitter, Event } from '@theia/core/lib/common/event'; import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable'; import { TaskCustomization, TaskConfiguration, TaskConfigurationScope } from '../common/task-protocol'; import { PreferenceProvider, PreferenceProviderDataChanges, PreferenceProviderDataChange } from '@theia/core/lib/browser'; +import { Is } from '@theia/core/lib/common/is'; /** * Holds the task configurations associated with a particular file. Uses an editor model to facilitate @@ -80,11 +81,9 @@ export class TaskConfigurationModel implements Disposable { const configurations: (TaskCustomization | TaskConfiguration)[] = []; // eslint-disable-next-line @typescript-eslint/no-explicit-any const { configUri, value } = this.preferences.resolve('tasks', this.getWorkspaceFolder()); - if (value && typeof value === 'object' && 'tasks' in value) { - if (Array.isArray(value.tasks)) { - for (const taskConfig of value.tasks) { - configurations.push(taskConfig); - } + if (Is.object(value) && Array.isArray(value.tasks)) { + for (const taskConfig of value.tasks) { + configurations.push(taskConfig); } } return { diff --git a/packages/toolbar/src/browser/toolbar-constants.ts b/packages/toolbar/src/browser/toolbar-constants.ts index 507b53250c7b5..38deac3e8b914 100644 --- a/packages/toolbar/src/browser/toolbar-constants.ts +++ b/packages/toolbar/src/browser/toolbar-constants.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** -import { Command, MenuPath, nls } from '@theia/core'; +import { Command, Is, MenuPath, nls } from '@theia/core'; import { CommonCommands } from '@theia/core/lib/browser'; import URI from '@theia/core/lib/common/uri'; import { UserStorageUri } from '@theia/userstorage/lib/browser'; @@ -74,6 +74,6 @@ export namespace ToolbarMenus { export type ReactInteraction = React.MouseEvent | React.KeyboardEvent; export namespace ReactKeyboardEvent { export function is(obj: unknown): obj is React.KeyboardEvent { - return typeof obj === 'object' && !!obj && 'key' in obj; + return Is.object(obj) && 'key' in obj; } } diff --git a/packages/workspace/src/browser/workspace-schema-updater.ts b/packages/workspace/src/browser/workspace-schema-updater.ts index 8556243d92372..7e585177a06d3 100644 --- a/packages/workspace/src/browser/workspace-schema-updater.ts +++ b/packages/workspace/src/browser/workspace-schema-updater.ts @@ -16,7 +16,7 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { JsonSchemaContribution, JsonSchemaRegisterContext } from '@theia/core/lib/browser/json-schema-store'; -import { InMemoryResources } from '@theia/core/lib/common'; +import { InMemoryResources, Is } from '@theia/core/lib/common'; import { IJSONSchema } from '@theia/core/lib/common/json-schema'; import URI from '@theia/core/lib/common/uri'; import { Deferred } from '@theia/core/lib/common/promise-util'; @@ -114,12 +114,11 @@ export class WorkspaceSchemaUpdater implements JsonSchemaContribution { export type WorkspaceSchema = Required>; export namespace WorkspaceSchema { - export const is = (candidate: unknown): candidate is WorkspaceSchema => !!candidate - && typeof candidate === 'object' - && 'properties' in candidate - && typeof (candidate as WorkspaceSchema).properties === 'object' - && 'required' in candidate - && Array.isArray((candidate as WorkspaceSchema).required); + export function is(candidate: unknown): candidate is WorkspaceSchema { + return Is.object(candidate) + && typeof candidate.properties === 'object' + && Array.isArray(candidate.required); + } } export const workspaceSchemaId = 'vscode://schemas/workspace';