diff --git a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts index 4b2303a91..6e80b4379 100644 --- a/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts +++ b/src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts @@ -15,6 +15,7 @@ import { AssignContributorForm, AutocompleteSource, ContributorsState, + Types, UserDto, UsersService } from '@app/shared'; @@ -85,7 +86,7 @@ export class ContributorsPageComponent implements OnInit { if (value) { let user = value.user; - if (user instanceof UserDto) { + if (Types.is(user, UserDto)) { user = user.id; } diff --git a/src/Squidex/app/framework/angular/forms/control-errors.component.ts b/src/Squidex/app/framework/angular/forms/control-errors.component.ts index b14a04c73..c6c2cb653 100644 --- a/src/Squidex/app/framework/angular/forms/control-errors.component.ts +++ b/src/Squidex/app/framework/angular/forms/control-errors.component.ts @@ -9,7 +9,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Host, Input, OnC import { AbstractControl, FormGroupDirective } from '@angular/forms'; import { Observable, Subscription } from 'rxjs'; -import { fadeAnimation } from '@app/framework/internal'; +import { fadeAnimation, Types } from '@app/framework/internal'; const DEFAULT_ERRORS: { [key: string]: string } = { required: '{field} is required.', @@ -71,7 +71,7 @@ export class ControlErrorsComponent implements OnChanges, OnDestroy { if (this.fieldName) { this.displayFieldName = this.fieldName; } else if (this.for) { - if (typeof this.for === 'string') { + if (Types.isString(this.for)) { this.displayFieldName = this.for.substr(0, 1).toUpperCase() + this.for.substr(1); } else { this.displayFieldName = 'field'; @@ -80,7 +80,7 @@ export class ControlErrorsComponent implements OnChanges, OnDestroy { let control: AbstractControl | null = null; - if (typeof this.for === 'string') { + if (Types.isString(this.for)) { control = this.formGroupDirective.form.controls[this.for]; } else { control = this.for; diff --git a/src/Squidex/app/framework/angular/forms/copy.directive.ts b/src/Squidex/app/framework/angular/forms/copy.directive.ts index 1f6d593b7..a46d78164 100644 --- a/src/Squidex/app/framework/angular/forms/copy.directive.ts +++ b/src/Squidex/app/framework/angular/forms/copy.directive.ts @@ -40,7 +40,7 @@ export class CopyDirective { element.focus(); - if (element instanceof HTMLInputElement) { + if (Types.is(element, HTMLInputElement)) { element.setSelectionRange(0, element.value.length); } @@ -56,7 +56,7 @@ export class CopyDirective { currentFocus.focus(); } - if (element instanceof HTMLInputElement) { + if (Types.is(element, HTMLInputElement)) { element.setSelectionRange(prevSelectionStart!, prevSelectionEnd!); } } diff --git a/src/Squidex/app/framework/angular/forms/validators.ts b/src/Squidex/app/framework/angular/forms/validators.ts index 7fe3d8615..fabd1bbc9 100644 --- a/src/Squidex/app/framework/angular/forms/validators.ts +++ b/src/Squidex/app/framework/angular/forms/validators.ts @@ -18,7 +18,7 @@ export module ValidatorsEx { let regeExp: RegExp; let regexStr: string; - if (typeof regex === 'string') { + if (Types.isString(regex)) { regexStr = `^${regex}$`; regeExp = new RegExp(regexStr); } else { diff --git a/src/Squidex/app/framework/angular/http/http-extensions-impl.ts b/src/Squidex/app/framework/angular/http/http-extensions-impl.ts index 498776a54..8fad99a21 100644 --- a/src/Squidex/app/framework/angular/http/http-extensions-impl.ts +++ b/src/Squidex/app/framework/angular/http/http-extensions-impl.ts @@ -9,7 +9,7 @@ import { HttpClient, HttpErrorResponse, HttpHeaders, HttpResponse } from '@angul import { Observable } from 'rxjs'; import { ErrorDto } from './../../utils/error'; - +import { Types} from './../../utils/types'; import { Version, Versioned } from './../../utils/version'; export module HTTP { @@ -64,9 +64,9 @@ export function pretifyError(message: string): Observable { return this.catch((response: HttpErrorResponse) => { let result: ErrorDto | null = null; - if (!(response.error instanceof Error)) { + if (!Types.is(response.error, Error)) { try { - const errorDto = typeof response.error === 'object' ? response.error : JSON.parse(response.error); + const errorDto = Types.isObject(response.error) ? response.error : JSON.parse(response.error); if (response.status === 412) { result = new ErrorDto(response.status, 'Failed to make the update. Another user has made a change. Please reload.'); diff --git a/src/Squidex/app/framework/angular/modals/modal-view.directive.ts b/src/Squidex/app/framework/angular/modals/modal-view.directive.ts index 636c6330e..895a8873f 100644 --- a/src/Squidex/app/framework/angular/modals/modal-view.directive.ts +++ b/src/Squidex/app/framework/angular/modals/modal-view.directive.ts @@ -8,7 +8,7 @@ import { Directive, EmbeddedViewRef, Input, OnChanges, OnDestroy, Renderer2, SimpleChanges, TemplateRef, ViewContainerRef } from '@angular/core'; import { Subscription } from 'rxjs'; -import { ModalView } from '@app/framework/internal'; +import { ModalView, Types } from '@app/framework/internal'; import { RootViewComponent } from './root-view.component'; @@ -40,7 +40,7 @@ export class ModalViewDirective implements OnChanges, OnDestroy { public ngOnDestroy() { this.stopListening(); - if (this.modalView instanceof ModalView) { + if (Types.is(this.modalView, ModalView)) { this.modalView.hide(); } } @@ -55,7 +55,7 @@ export class ModalViewDirective implements OnChanges, OnDestroy { this.subscription = null; } - if (this.modalView instanceof ModalView) { + if (Types.is(this.modalView, ModalView)) { this.subscription = this.modalView.isOpen.subscribe(isOpen => { this.update(isOpen); diff --git a/src/Squidex/app/framework/services/analytics.service.ts b/src/Squidex/app/framework/services/analytics.service.ts index b68937b2c..e6ba06235 100644 --- a/src/Squidex/app/framework/services/analytics.service.ts +++ b/src/Squidex/app/framework/services/analytics.service.ts @@ -9,6 +9,7 @@ import { Injectable } from '@angular/core'; import { NavigationEnd, Router } from '@angular/router'; import { AnalyticsIdConfig } from '../configurations'; +import { Types } from './../utils/types'; import { ResourceLoaderService } from './resource-loader.service'; // tslint:disable:only-arrow-functions @@ -31,7 +32,7 @@ export class AnalyticsService { if (analyticsId && router && resourceLoader && window.location.hostname !== 'localhost') { this.gtag('config', analyticsId.value); - router.events.filter(e => e instanceof NavigationEnd) + router.events.filter(e => Types.is(e, NavigationEnd)) .subscribe(() => { this.gtag('config', analyticsId.value, { page_path: window.location.pathname }); }); diff --git a/src/Squidex/app/framework/services/dialog.service.ts b/src/Squidex/app/framework/services/dialog.service.ts index 72b9a0e00..b7d77d543 100644 --- a/src/Squidex/app/framework/services/dialog.service.ts +++ b/src/Squidex/app/framework/services/dialog.service.ts @@ -9,6 +9,7 @@ import { Injectable } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { ErrorDto } from './../utils/error'; +import { Types } from './../utils/types'; export const DialogServiceFactory = () => { return new DialogService(); @@ -64,7 +65,7 @@ export class DialogService { } public notifyError(error: string | ErrorDto) { - if (error instanceof ErrorDto) { + if (Types.is(error, ErrorDto)) { this.notify(Notification.error(error.displayMessage)); } else { this.notify(Notification.error(error)); diff --git a/src/Squidex/app/framework/state.ts b/src/Squidex/app/framework/state.ts index 03bca305b..a53b4c1e4 100644 --- a/src/Squidex/app/framework/state.ts +++ b/src/Squidex/app/framework/state.ts @@ -9,6 +9,7 @@ import { AbstractControl } from '@angular/forms'; import { BehaviorSubject, Observable } from 'rxjs'; import { ErrorDto } from './utils/error'; +import { Types } from './utils/types'; export interface FormState { submitted: boolean; @@ -69,7 +70,7 @@ export class Form { } private getError(error?: string | ErrorDto) { - if (error instanceof ErrorDto) { + if (Types.is(error, ErrorDto)) { return error.displayMessage; } else { return error; @@ -100,7 +101,7 @@ export class State { } public next(update: ((v: T) => T) | object) { - if (update instanceof Function) { + if (Types.isFunction(update)) { this.state.next(update(this.state.value)); } else { this.state.next(Object.assign({}, this.snapshot, update)); diff --git a/src/Squidex/app/framework/utils/types.spec.ts b/src/Squidex/app/framework/utils/types.spec.ts index 70fa846c8..f291b08c2 100644 --- a/src/Squidex/app/framework/utils/types.spec.ts +++ b/src/Squidex/app/framework/utils/types.spec.ts @@ -89,4 +89,17 @@ describe('Types', () => { expect(Types.isFunction([])).toBeFalsy(); }); -}); \ No newline at end of file + + it('should make type check', () => { + expect(Types.is(new MyClass(1), MyClass)).toBeTruthy(); + + expect(Types.is(1, MyClass)).toBeFalsy(); + }); +}); + +class MyClass { + constructor( + public readonly value: number + ) { + } +} \ No newline at end of file diff --git a/src/Squidex/app/framework/utils/types.ts b/src/Squidex/app/framework/utils/types.ts index d94c31a1c..437926303 100644 --- a/src/Squidex/app/framework/utils/types.ts +++ b/src/Squidex/app/framework/utils/types.ts @@ -46,6 +46,10 @@ export module Types { return value instanceof Date; } + export function is(x: any, c: new (...args: any[]) => TClass): x is TClass { + return x instanceof c; + } + export function isArrayOfNumber(value: any): value is Array { return isArrayOf(value, v => isNumber(v)); } diff --git a/src/Squidex/app/shared/components/asset.component.ts b/src/Squidex/app/shared/components/asset.component.ts index e9adb8739..919a56a30 100644 --- a/src/Squidex/app/shared/components/asset.component.ts +++ b/src/Squidex/app/shared/components/asset.component.ts @@ -18,6 +18,7 @@ import { fadeAnimation, ModalView, RenameAssetForm, + Types, UpdateAssetDto, Versioned } from '@app/shared/internal'; @@ -87,7 +88,7 @@ export class AssetComponent implements OnInit { if (initFile) { this.assetsService.uploadFile(this.appsState.appName, initFile, this.authState.user!.token, DateTime.now()) .subscribe(dto => { - if (dto instanceof AssetDto) { + if (Types.is(dto, AssetDto)) { this.emitLoaded(dto); } else { this.progress = dto; @@ -106,7 +107,7 @@ export class AssetComponent implements OnInit { if (files.length === 1) { this.assetsService.replaceFile(this.appsState.appName, this.asset.id, files[0], this.asset.version) .subscribe(dto => { - if (dto instanceof Versioned) { + if (Types.is(dto, Versioned)) { this.updateAsset(this.asset.update(dto.payload, this.authState.user!.token, dto.version), true); } else { this.setProgress(dto); diff --git a/src/Squidex/app/shared/services/assets.service.ts b/src/Squidex/app/shared/services/assets.service.ts index 5b2b9e147..7f58faa6b 100644 --- a/src/Squidex/app/shared/services/assets.service.ts +++ b/src/Squidex/app/shared/services/assets.service.ts @@ -14,6 +14,7 @@ import { ApiUrlConfig, DateTime, HTTP, + Types, Version, Versioned } from '@app/framework'; @@ -176,7 +177,7 @@ export class AssetsService { const percentDone = event.total ? Math.round(100 * event.loaded / event.total) : 0; return percentDone; - } else if (event instanceof HttpResponse) { + } else if (Types.is(event, HttpResponse)) { const response: any = event.body; const assetUrl = this.apiUrl.buildUrl(`api/assets/${response.id}`); @@ -256,7 +257,7 @@ export class AssetsService { const percentDone = event.total ? Math.round(100 * event.loaded / event.total) : 0; return percentDone; - } else if (event instanceof HttpResponse) { + } else if (Types.is(event, HttpResponse)) { const response: any = event.body; const replaced = new AssetReplacedDto( diff --git a/src/Squidex/app/shared/services/auth.service.ts b/src/Squidex/app/shared/services/auth.service.ts index b1f7ac20e..235c88461 100644 --- a/src/Squidex/app/shared/services/auth.service.ts +++ b/src/Squidex/app/shared/services/auth.service.ts @@ -15,7 +15,7 @@ import { WebStorageStateStore } from 'oidc-client'; -import { ApiUrlConfig } from '@app/framework'; +import { ApiUrlConfig, Types } from '@app/framework'; export class Profile { public get id(): string { @@ -163,7 +163,7 @@ export class AuthService { return observable.timeout(2000) .retryWhen(errors => errors - .mergeMap(e => e instanceof TimeoutError ? Observable.of(e) : Observable.throw(e)) + .mergeMap(e => Types.is(e, TimeoutError) ? Observable.of(e) : Observable.throw(e)) .delay(500) .take(5) .concat(Observable.throw(new Error('Retry limit exceeded.')))); diff --git a/src/Squidex/app/shared/state/schemas.state.ts b/src/Squidex/app/shared/state/schemas.state.ts index 963f2d1a0..ec1a9ba9f 100644 --- a/src/Squidex/app/shared/state/schemas.state.ts +++ b/src/Squidex/app/shared/state/schemas.state.ts @@ -17,6 +17,7 @@ import { Form, ImmutableArray, State, + Types, ValidatorsEx, Version } from '@app/framework'; @@ -370,7 +371,7 @@ export class SchemasState extends State { } const setPublished = (schema: SchemaDto | SchemaDetailsDto, publish: boolean, user: string, version: Version, now?: DateTime) => { - if (schema instanceof SchemaDetailsDto) { + if (Types.is(schema, SchemaDetailsDto)) { return new SchemaDetailsDto( schema.id, schema.name,