diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts index 30e4aa5d5..2c3b7560d 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts @@ -273,11 +273,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit { const defaultValues = { transcoding: { - resolutions: {} + resolutions: {} as { [id: string]: string } }, live: { transcoding: { - resolutions: {} + resolutions: {} as { [id: string]: string } } } } diff --git a/client/src/app/+admin/overview/users/user-edit/user-edit.ts b/client/src/app/+admin/overview/users/user-edit/user-edit.ts index 5d6c6a91e..9547da2d1 100644 --- a/client/src/app/+admin/overview/users/user-edit/user-edit.ts +++ b/client/src/app/+admin/overview/users/user-edit/user-edit.ts @@ -53,8 +53,8 @@ export abstract class UserEdit extends FormReactive implements OnInit { this.serverService.getServerLocale() .subscribe(translations => { if (authUser.role.id === UserRole.ADMINISTRATOR) { - this.roles = Object.keys(USER_ROLE_LABELS) - .map(key => ({ value: key.toString(), label: peertubeTranslate(USER_ROLE_LABELS[key], translations) })) + this.roles = Object.entries(USER_ROLE_LABELS) + .map(([ key, value ]) => ({ value: key.toString(), label: peertubeTranslate(value, translations) })) return } diff --git a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts index 2fdc14d85..3fa1c56dc 100644 --- a/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts +++ b/client/src/app/+admin/plugins/plugin-list-installed/plugin-list-installed.component.ts @@ -104,7 +104,7 @@ export class PluginListInstalledComponent implements OnInit { } isUninstalling (plugin: PeerTubePlugin) { - return !!this.uninstall[this.getPluginKey(plugin)] + return !!this.uninstalling[this.getPluginKey(plugin)] } isTheme (plugin: PeerTubePlugin) { diff --git a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts index 769ab647a..8faba676e 100644 --- a/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts +++ b/client/src/app/+my-account/my-account-settings/my-account-notification-preferences/my-account-notification-preferences.component.ts @@ -141,11 +141,11 @@ export class MyAccountNotificationPreferencesComponent implements OnInit { } private loadNotificationSettings () { - for (const key of Object.keys(this.user.notificationSettings)) { + for (const key of Object.keys(this.user.notificationSettings) as (keyof UserNotificationSetting)[]) { const value = this.user.notificationSettings[key] - this.emailNotifications[key] = value & UserNotificationSettingValue.EMAIL + this.emailNotifications[key] = !!(value & UserNotificationSettingValue.EMAIL) - this.webNotifications[key] = value & UserNotificationSettingValue.WEB + this.webNotifications[key] = !!(value & UserNotificationSettingValue.WEB) } } } diff --git a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts index 74dbe222d..1f7287f44 100644 --- a/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts +++ b/client/src/app/+my-library/my-video-channel-syncs/my-video-channel-syncs.component.ts @@ -124,7 +124,7 @@ export class MyVideoChannelSyncsComponent extends RestTable implements OnInit { return '/my-library/video-channel-syncs/create' } - getSyncStateClass (stateId: number) { + getSyncStateClass (stateId: VideoChannelSyncState) { return [ 'pt-badge', MyVideoChannelSyncsComponent.STATE_CLASS_BY_ID[stateId] ] } diff --git a/client/src/app/+my-library/my-videos/my-videos.component.ts b/client/src/app/+my-library/my-videos/my-videos.component.ts index 46dd304ba..b618b3f88 100644 --- a/client/src/app/+my-library/my-videos/my-videos.component.ts +++ b/client/src/app/+my-library/my-videos/my-videos.component.ts @@ -171,15 +171,15 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook { .subscribe(result => { this.videosContainedInPlaylists = Object.keys(result).reduce((acc, videoId) => ({ ...acc, - [videoId]: uniqBy(result[videoId], (p: VideoExistInPlaylist) => p.playlistId) + [videoId]: uniqBy(result[+videoId], (p: VideoExistInPlaylist) => p.playlistId) }), this.videosContainedInPlaylists) }) } async deleteSelectedVideos () { - const toDeleteVideosIds = Object.keys(this.selection) - .filter(k => this.selection[k] === true) - .map(k => parseInt(k, 10)) + const toDeleteVideosIds = Object.entries(this.selection) + .filter(([ _k, v ]) => v === true) + .map(([ k, _v ]) => parseInt(k, 10)) const res = await this.confirmService.confirm( prepareIcu($localize`Do you really want to delete {length, plural, =1 {this video} other {{length} videos}}?`)( diff --git a/client/src/app/+search/search-filters.component.ts b/client/src/app/+search/search-filters.component.ts index f9de04706..a6fc51383 100644 --- a/client/src/app/+search/search-filters.component.ts +++ b/client/src/app/+search/search-filters.component.ts @@ -118,11 +118,11 @@ export class SearchFiltersComponent implements OnInit { this.onDurationOrPublishedUpdated() } - resetField (fieldName: string, value?: any) { - this.advancedSearch[fieldName] = value + resetField (fieldName: keyof AdvancedSearch, value?: any) { + (this.advancedSearch as any)[fieldName] = value } - resetLocalField (fieldName: string, value?: any) { + resetLocalField (fieldName: keyof SearchFiltersComponent, value?: any) { this[fieldName] = value this.onDurationOrPublishedUpdated() } diff --git a/client/src/app/+stats/video/video-stats.component.ts b/client/src/app/+stats/video/video-stats.component.ts index 18312ec33..fa5e33ab6 100644 --- a/client/src/app/+stats/video/video-stats.component.ts +++ b/client/src/app/+stats/video/video-stats.component.ts @@ -47,7 +47,7 @@ export class VideoStatsComponent implements OnInit { chartHeight = '300px' chartWidth: string = null - availableCharts: { id: string, label: string, zoomEnabled: boolean }[] = [] + availableCharts: { id: ActiveGraphId, label: string, zoomEnabled: boolean }[] = [] activeGraphId: ActiveGraphId = 'viewers' video: VideoDetails diff --git a/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts b/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts index db1ef8d73..214bde680 100644 --- a/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts +++ b/client/src/app/+videos/+video-edit/shared/video-edit-utils.ts @@ -8,11 +8,11 @@ function hydrateFormFromVideo (formGroup: FormGroup, video: VideoEdit, thumbnail const objects = [ { - url: 'thumbnailUrl', + url: 'thumbnailUrl' as 'thumbnailUrl', name: 'thumbnailfile' }, { - url: 'previewUrl', + url: 'previewUrl' as 'previewUrl', name: 'previewfile' } ] diff --git a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts index 28edcfdcb..96bdb28c9 100644 --- a/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts +++ b/client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts @@ -263,8 +263,8 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy { this.syndicationItems = this.videoCommentService.getVideoCommentsFeeds(this.video) this.loadMoreThreads() - if (this.activatedRoute.params['threadId']) { - this.processHighlightedThread(+this.activatedRoute.params['threadId']) + if (this.activatedRoute.snapshot.params['threadId']) { + this.processHighlightedThread(+this.activatedRoute.snapshot.params['threadId']) } } } diff --git a/client/src/app/+videos/video-list/overview/overview.service.ts b/client/src/app/+videos/video-list/overview/overview.service.ts index 12d2aa1cb..4a7d9c7c5 100644 --- a/client/src/app/+videos/video-list/overview/overview.service.ts +++ b/client/src/app/+videos/video-list/overview/overview.service.ts @@ -5,6 +5,7 @@ import { Injectable } from '@angular/core' import { RestExtractor, ServerService } from '@app/core' import { immutableAssign } from '@app/helpers' import { VideoService } from '@app/shared/shared-main' +import { objectKeysTyped } from '@shared/core-utils' import { peertubeTranslate } from '@shared/core-utils/i18n' import { VideosOverview as VideosOverviewServer } from '@shared/models' import { environment } from '../../../../environments/environment' @@ -42,7 +43,7 @@ export class OverviewService { } // Build videos objects - for (const key of Object.keys(serverVideosOverview)) { + for (const key of objectKeysTyped(serverVideosOverview)) { for (const object of serverVideosOverview[key]) { observables.push( of(object.videos) @@ -50,7 +51,9 @@ export class OverviewService { switchMap(videos => this.videosService.extractVideos({ total: 0, data: videos })), map(result => result.data), tap(videos => { - videosOverviewResult[key].push(immutableAssign(object, { videos })) + // FIXME: typings & lint + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + videosOverviewResult[key].push(immutableAssign(object, { videos }) as any) }) ) ) diff --git a/client/src/app/core/server/server.service.ts b/client/src/app/core/server/server.service.ts index 9f191f0a6..fc269749b 100644 --- a/client/src/app/core/server/server.service.ts +++ b/client/src/app/core/server/server.service.ts @@ -193,7 +193,8 @@ export class ServerService { } private loadHTMLConfigLocally () { - const configString = window['PeerTubeServerConfig'] + // FIXME: typings + const configString = (window as any)['PeerTubeServerConfig'] if (!configString) { throw new Error('Could not find PeerTubeServerConfig in HTML') } diff --git a/client/src/app/core/users/user-local-storage.service.ts b/client/src/app/core/users/user-local-storage.service.ts index 1e629249a..a87f3b98a 100644 --- a/client/src/app/core/users/user-local-storage.service.ts +++ b/client/src/app/core/users/user-local-storage.service.ts @@ -4,7 +4,8 @@ import { Injectable } from '@angular/core' import { AuthService, AuthStatus } from '@app/core/auth' import { getBoolOrDefault } from '@root-helpers/local-storage-utils' import { logger } from '@root-helpers/logger' -import { UserLocalStorageKeys, OAuthUserTokens } from '@root-helpers/users' +import { OAuthUserTokens, UserLocalStorageKeys } from '@root-helpers/users' +import { objectKeysTyped } from '@shared/core-utils' import { UserRole, UserUpdateMe } from '@shared/models' import { NSFWPolicyType } from '@shared/models/videos' import { ServerService } from '../server' @@ -122,7 +123,7 @@ export class UserLocalStorageService { } setUserInfo (profile: UserUpdateMe) { - const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = { + const localStorageKeys = { nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY, p2pEnabled: UserLocalStorageKeys.P2P_ENABLED, autoPlayVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO, @@ -132,7 +133,7 @@ export class UserLocalStorageService { videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES } - const obj = Object.keys(localStorageKeys) + const obj: [ string, string | boolean | string[] ][] = objectKeysTyped(localStorageKeys) .filter(key => key in profile) .map(key => ([ localStorageKeys[key], profile[key] ])) diff --git a/client/src/app/core/users/user.model.ts b/client/src/app/core/users/user.model.ts index 2d783145f..d57608f1c 100644 --- a/client/src/app/core/users/user.model.ts +++ b/client/src/app/core/users/user.model.ts @@ -1,4 +1,5 @@ import { Account } from '@app/shared/shared-main/account/account.model' +import { objectKeysTyped } from '@shared/core-utils' import { hasUserRight } from '@shared/core-utils/users' import { ActorImage, @@ -130,8 +131,9 @@ export class User implements UserServerModel { } patch (obj: UserServerModel) { - for (const key of Object.keys(obj)) { - this[key] = obj[key] + for (const key of objectKeysTyped(obj)) { + // FIXME: typings + (this as any)[key] = obj[key] } if (obj.account !== undefined) { diff --git a/client/src/app/menu/language-chooser.component.ts b/client/src/app/menu/language-chooser.component.ts index b42e41855..f7ae69717 100644 --- a/client/src/app/menu/language-chooser.component.ts +++ b/client/src/app/menu/language-chooser.component.ts @@ -1,6 +1,7 @@ import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers' import { NgbModal } from '@ng-bootstrap/ng-bootstrap' +import { objectKeysTyped } from '@shared/core-utils' import { getCompleteLocale, getShortLocale, I18N_LOCALES } from '@shared/core-utils/i18n' @Component({ @@ -17,8 +18,8 @@ export class LanguageChooserComponent { private modalService: NgbModal, @Inject(LOCALE_ID) private localeId: string ) { - const l = Object.keys(I18N_LOCALES) - .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) })) + const l = objectKeysTyped(I18N_LOCALES) + .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) })) this.languages = sortBy(l, 'label') } @@ -35,7 +36,7 @@ export class LanguageChooserComponent { const english = 'English' const locale = isOnDevLocale() ? getDevLocale() : getCompleteLocale(this.localeId) - if (locale) return I18N_LOCALES[locale] || english + if (locale) return I18N_LOCALES[locale as keyof typeof I18N_LOCALES] || english return english } } diff --git a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts index f1c1aa03f..ab2e02ad7 100644 --- a/client/src/app/shared/shared-actor-image/actor-avatar.component.ts +++ b/client/src/app/shared/shared-actor-image/actor-avatar.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnChanges, OnInit } from '@angular/core' import { VideoChannel } from '../shared-main' import { Account } from '../shared-main/account/account.model' +import { objectKeysTyped } from '@shared/core-utils' type ActorInput = { name: string @@ -154,8 +155,8 @@ export class ActorAvatarComponent implements OnInit, OnChanges { 'wxyz': 'dark-blue' } - const theme = Object.keys(themes) - .find(chars => chars.includes(initialLowercase)) + const theme = objectKeysTyped(themes) + .find(chars => chars.includes(initialLowercase)) return themes[theme] || 'blue' } diff --git a/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts b/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts index 208dba721..a12907055 100644 --- a/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts +++ b/client/src/app/shared/shared-custom-markup/dynamic-element.service.ts @@ -10,6 +10,7 @@ import { SimpleChanges, Type } from '@angular/core' +import { objectKeysTyped } from '@shared/core-utils' @Injectable() export class DynamicElementService { @@ -41,12 +42,12 @@ export class DynamicElementService { setModel (componentRef: ComponentRef, attributes: Partial) { const changes: SimpleChanges = {} - for (const key of Object.keys(attributes)) { + for (const key of objectKeysTyped(attributes)) { const previousValue = componentRef.instance[key] const newValue = attributes[key] componentRef.instance[key] = newValue - changes[key] = new SimpleChange(previousValue, newValue, previousValue === undefined) + changes[key as string] = new SimpleChange(previousValue, newValue, previousValue === undefined) } const component = componentRef.instance diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts index 21774b7aa..bd93929c9 100644 --- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts +++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts @@ -2,6 +2,7 @@ import { finalize } from 'rxjs/operators' import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import { AuthService, Notifier } from '@app/core' import { FindInBulkService } from '@app/shared/shared-search' +import { objectKeysTyped } from '@shared/core-utils' import { Video } from '../../shared-main' import { MiniatureDisplayOptions } from '../../shared-video-miniature' import { CustomMarkupComponent } from './shared' @@ -47,7 +48,7 @@ export class VideoMiniatureMarkupComponent implements CustomMarkupComponent, OnI ngOnInit () { if (this.onlyDisplayTitle) { - for (const key of Object.keys(this.displayOptions)) { + for (const key of objectKeysTyped(this.displayOptions)) { this.displayOptions[key] = false } } diff --git a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts index 7c2e7db6a..81363be87 100644 --- a/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts +++ b/client/src/app/shared/shared-custom-markup/peertube-custom-tags/videos-list-markup.component.ts @@ -1,6 +1,7 @@ import { finalize } from 'rxjs/operators' import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' import { AuthService, Notifier } from '@app/core' +import { objectKeysTyped } from '@shared/core-utils' import { VideoSortField } from '@shared/models' import { Video, VideoService } from '../../shared-main' import { MiniatureDisplayOptions } from '../../shared-video-miniature' @@ -66,7 +67,7 @@ export class VideosListMarkupComponent implements CustomMarkupComponent, OnInit ngOnInit () { if (this.onlyDisplayTitle) { - for (const key of Object.keys(this.displayOptions)) { + for (const key of objectKeysTyped(this.displayOptions)) { this.displayOptions[key] = false } } diff --git a/client/src/app/shared/shared-forms/form-validator.service.ts b/client/src/app/shared/shared-forms/form-validator.service.ts index 897008242..14ee044b5 100644 --- a/client/src/app/shared/shared-forms/form-validator.service.ts +++ b/client/src/app/shared/shared-forms/form-validator.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core' import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' +import { objectKeysTyped } from '@shared/core-utils' import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive.service' @@ -47,13 +48,14 @@ export class FormValidatorService { obj: BuildFormArgument, defaultValues: BuildFormDefaultValues = {} ) { - for (const name of Object.keys(obj)) { + for (const name of objectKeysTyped(obj)) { formErrors[name] = '' const field = obj[name] if (this.isRecursiveField(field)) { this.updateFormGroup( - form[name], + // FIXME: typings + (form as any)[name], formErrors[name] as FormReactiveErrors, validationMessages[name] as FormReactiveValidationMessages, obj[name] as BuildFormArgument, @@ -67,7 +69,7 @@ export class FormValidatorService { const defaultValue = defaultValues[name] || '' form.addControl( - name, + name + '', new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[]) ) } @@ -75,7 +77,8 @@ export class FormValidatorService { updateTreeValidity (group: FormGroup | FormArray): void { for (const key of Object.keys(group.controls)) { - const abstractControl = group.controls[key] as FormControl + // FIXME: typings + const abstractControl = (group.controls as any)[key] as FormControl if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) { this.updateTreeValidity(abstractControl) diff --git a/client/src/app/shared/shared-icons/global-icon.component.ts b/client/src/app/shared/shared-icons/global-icon.component.ts index 96179cbe6..eea460831 100644 --- a/client/src/app/shared/shared-icons/global-icon.component.ts +++ b/client/src/app/shared/shared-icons/global-icon.component.ts @@ -112,7 +112,7 @@ export class GlobalIconComponent implements OnInit { } } - private getSVGContent (options: { name: string }) { + private getSVGContent (options: { name: GlobalIconName }) { return icons[options.name] } } diff --git a/client/src/app/shared/shared-instance/instance.service.ts b/client/src/app/shared/shared-instance/instance.service.ts index 2defffbbe..3088f0899 100644 --- a/client/src/app/shared/shared-instance/instance.service.ts +++ b/client/src/app/shared/shared-instance/instance.service.ts @@ -3,6 +3,7 @@ import { catchError, map } from 'rxjs/operators' import { HttpClient } from '@angular/common/http' import { Injectable } from '@angular/core' import { MarkdownService, RestExtractor, ServerService } from '@app/core' +import { objectKeysTyped } from '@shared/core-utils' import { peertubeTranslate } from '@shared/core-utils/i18n' import { About } from '@shared/models' import { environment } from '../../../environments/environment' @@ -55,7 +56,7 @@ export class InstanceService { hardwareInformation: '' } - for (const key of Object.keys(html)) { + for (const key of objectKeysTyped(html)) { html[key] = await this.markdownService.enhancedMarkdownToHTML({ markdown: about.instance[key] }) } diff --git a/client/src/app/shared/shared-main/misc/help.component.ts b/client/src/app/shared/shared-main/misc/help.component.ts index 37e2abd97..80fe0e160 100644 --- a/client/src/app/shared/shared-main/misc/help.component.ts +++ b/client/src/app/shared/shared-main/misc/help.component.ts @@ -77,7 +77,7 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit { } private createMarkdownList (rules: string[]) { - const rulesToText = { + const rulesToText: { [id: string]: string } = { emphasis: $localize`Emphasis`, link: $localize`Links`, newline: $localize`New lines`, diff --git a/client/src/app/shared/shared-main/video/video-edit.model.ts b/client/src/app/shared/shared-main/video/video-edit.model.ts index 91d57cb6b..47eee80d8 100644 --- a/client/src/app/shared/shared-main/video/video-edit.model.ts +++ b/client/src/app/shared/shared-main/video/video-edit.model.ts @@ -1,6 +1,7 @@ import { getAbsoluteAPIUrl } from '@app/helpers' import { VideoPrivacy, VideoScheduleUpdate, VideoUpdate } from '@shared/models' import { VideoDetails } from './video-details.model' +import { objectKeysTyped } from '@shared/core-utils' export class VideoEdit implements VideoUpdate { static readonly SPECIAL_SCHEDULED_PRIVACY = -1 @@ -65,8 +66,9 @@ export class VideoEdit implements VideoUpdate { } patch (values: { [ id: string ]: any }) { - Object.keys(values).forEach((key) => { - this[key] = values[key] + objectKeysTyped(values).forEach(key => { + // FIXME: typings + (this as any)[key] = values[key] }) // If schedule publication, the video is private and will be changed to public privacy diff --git a/client/src/app/shared/shared-share-modal/video-share.component.ts b/client/src/app/shared/shared-share-modal/video-share.component.ts index 1b69aa2d0..32f900f15 100644 --- a/client/src/app/shared/shared-share-modal/video-share.component.ts +++ b/client/src/app/shared/shared-share-modal/video-share.component.ts @@ -106,7 +106,8 @@ export class VideoShareComponent { includeVideoInPlaylist: false }, { set: (target, prop, value) => { - target[prop] = value + // FIXME: typings + (target as any)[prop] = value if (prop === 'embedP2P') { // Auto enabled warning title if P2P is enabled diff --git a/client/src/app/shared/shared-video-miniature/video-download.component.ts b/client/src/app/shared/shared-video-miniature/video-download.component.ts index 4135542dc..cac82d8d0 100644 --- a/client/src/app/shared/shared-video-miniature/video-download.component.ts +++ b/client/src/app/shared/shared-video-miniature/video-download.component.ts @@ -1,4 +1,4 @@ -import { mapValues, pick } from 'lodash-es' +import { mapValues } from 'lodash-es' import { firstValueFrom } from 'rxjs' import { tap } from 'rxjs/operators' import { Component, ElementRef, Inject, LOCALE_ID, ViewChild } from '@angular/core' @@ -6,11 +6,12 @@ import { HooksService } from '@app/core' import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' import { logger } from '@root-helpers/logger' import { videoRequiresAuth } from '@root-helpers/video' +import { objectKeysTyped, pick } from '@shared/core-utils' import { VideoCaption, VideoFile, VideoPrivacy } from '@shared/models' import { BytesPipe, NumberFormatterPipe, VideoDetails, VideoFileTokenService, VideoService } from '../shared-main' type DownloadType = 'video' | 'subtitles' -type FileMetadata = { [key: string]: { label: string, value: string } } +type FileMetadata = { [key: string]: { label: string, value: string | number } } @Component({ selector: 'my-video-download', @@ -218,10 +219,10 @@ export class VideoDownloadComponent { const keyToTranslateFunction = { encoder: (value: string) => ({ label: $localize`Encoder`, value }), format_long_name: (value: string) => ({ label: $localize`Format name`, value }), - size: (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }), - bit_rate: (value: number) => ({ + size: (value: number | string) => ({ label: $localize`Size`, value: this.bytesPipe.transform(+value, 2) }), + bit_rate: (value: number | string) => ({ label: $localize`Bitrate`, - value: `${this.numbersPipe.transform(value)}bps` + value: `${this.numbersPipe.transform(+value)}bps` }) } @@ -230,8 +231,8 @@ export class VideoDownloadComponent { delete sanitizedFormat.tags return mapValues( - pick(sanitizedFormat, Object.keys(keyToTranslateFunction)), - (val, key) => keyToTranslateFunction[key](val) + pick(sanitizedFormat, objectKeysTyped(keyToTranslateFunction)), + (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val) ) } @@ -242,29 +243,29 @@ export class VideoDownloadComponent { let keyToTranslateFunction = { codec_long_name: (value: string) => ({ label: $localize`Codec`, value }), profile: (value: string) => ({ label: $localize`Profile`, value }), - bit_rate: (value: number) => ({ + bit_rate: (value: number | string) => ({ label: $localize`Bitrate`, - value: `${this.numbersPipe.transform(value)}bps` + value: `${this.numbersPipe.transform(+value)}bps` }) } if (type === 'video') { keyToTranslateFunction = Object.assign(keyToTranslateFunction, { - width: (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }), + width: (value: string | number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }), display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }), avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }), pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value }) }) } else { keyToTranslateFunction = Object.assign(keyToTranslateFunction, { - sample_rate: (value: number) => ({ label: $localize`Sample rate`, value }), - channel_layout: (value: number) => ({ label: $localize`Channel Layout`, value }) + sample_rate: (value: string | number) => ({ label: $localize`Sample rate`, value }), + channel_layout: (value: string | number) => ({ label: $localize`Channel Layout`, value }) }) } return mapValues( pick(stream, Object.keys(keyToTranslateFunction)), - (val, key) => keyToTranslateFunction[key](val) + (val: string, key: keyof typeof keyToTranslateFunction) => keyToTranslateFunction[key](val) ) } diff --git a/client/src/app/shared/shared-video-miniature/video-filters.model.ts b/client/src/app/shared/shared-video-miniature/video-filters.model.ts index 6b4b72c75..4db73b25a 100644 --- a/client/src/app/shared/shared-video-miniature/video-filters.model.ts +++ b/client/src/app/shared/shared-video-miniature/video-filters.model.ts @@ -84,7 +84,7 @@ export class VideoFilters { if (specificKey && specificKey !== key) continue // FIXME: typings - this[key as any] = value + (this as any)[key] = value } this.buildActiveFilters() diff --git a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts index 460a0080e..86fe502e2 100644 --- a/client/src/app/shared/shared-video-miniature/videos-selection.component.ts +++ b/client/src/app/shared/shared-video-miniature/videos-selection.component.ts @@ -2,6 +2,7 @@ import { Observable, Subject } from 'rxjs' import { AfterContentInit, Component, ContentChildren, EventEmitter, Input, Output, QueryList, TemplateRef } from '@angular/core' import { ComponentPagination, Notifier, User } from '@app/core' import { logger } from '@root-helpers/logger' +import { objectKeysTyped } from '@shared/core-utils' import { ResultList, VideosExistInPlaylists, VideoSortField } from '@shared/models' import { PeerTubeTemplateDirective, Video } from '../shared-main' import { MiniatureDisplayOptions } from './video-miniature.component' @@ -93,7 +94,7 @@ export class VideosSelectionComponent implements AfterContentInit { } isInSelectionMode () { - return Object.keys(this._selection).some(k => this._selection[k] === true) + return objectKeysTyped(this._selection).some(k => this._selection[k] === true) } videoById (index: number, video: Video) { diff --git a/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts b/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts index 63f27eecf..d05d6193c 100644 --- a/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts +++ b/client/src/assets/player/shared/p2p-media-loader/hls-plugin.ts @@ -349,7 +349,7 @@ class Html5Hlsjs { } private _oneLevelObjClone (obj: { [ id: string ]: any }) { - const result = {} + const result: { [id: string]: any } = {} const objKeys = Object.keys(obj) for (let i = 0; i < objKeys.length; i++) { result[objKeys[i]] = obj[objKeys[i]] diff --git a/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts b/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts index 6e9bcf103..44a31bfb4 100644 --- a/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts +++ b/client/src/assets/player/shared/p2p-media-loader/segment-validator.ts @@ -79,7 +79,7 @@ function fetchSha256Segments (options: { segmentsSha256Url: string authorizationHeader: () => string requiresAuth: boolean -}) { +}): Promise { const { serverUrl, segmentsSha256Url, requiresAuth, authorizationHeader } = options const headers = requiresAuth && isSameOrigin(serverUrl, segmentsSha256Url) diff --git a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts index 46d009410..3dde44a60 100644 --- a/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts +++ b/client/src/assets/player/shared/webtorrent/webtorrent-plugin.ts @@ -339,7 +339,7 @@ class WebTorrentPlugin extends Plugin { if (err.message.indexOf('incorrect info hash') !== -1) { logger.error('Incorrect info hash detected, falling back to torrent file.') const newOptions = { forcePlay: true, seek: options.seek } - return this.addTorrent(this.torrent['xs'], previousVideoFile, newOptions, done) + return this.addTorrent((this.torrent as any)['xs'], previousVideoFile, newOptions, done) } // Remote instance is down @@ -582,7 +582,7 @@ class WebTorrentPlugin extends Plugin { private stopTorrent (torrent: WebTorrent.Torrent) { torrent.pause() // Pause does not remove actual peers (in particular the webseed peer) - torrent.removePeer(torrent['ws']) + torrent.removePeer((torrent as any)['ws']) } private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) { diff --git a/client/src/root-helpers/plugins-manager.ts b/client/src/root-helpers/plugins-manager.ts index f99c7598d..fd7b5233b 100644 --- a/client/src/root-helpers/plugins-manager.ts +++ b/client/src/root-helpers/plugins-manager.ts @@ -317,7 +317,7 @@ async function dynamicImport (url: string) { const script = document.createElement('script') const destructor = () => { - delete window[vector] + delete window[vector as any] script.onerror = null script.onload = null script.remove() @@ -333,7 +333,7 @@ async function dynamicImport (url: string) { destructor() } script.onload = () => { - resolve(window[vector]) + resolve(window[vector as any]) destructor() } const loader = `import * as m from "${url}"; window.${vector} = m;` // export Module diff --git a/client/src/root-helpers/video.ts b/client/src/root-helpers/video.ts index 01feddbdc..9022b908b 100644 --- a/client/src/root-helpers/video.ts +++ b/client/src/root-helpers/video.ts @@ -21,7 +21,7 @@ function buildVideoOrPlaylistEmbed (options: { const wrapper = document.createElement('div') wrapper.style.position = 'relative' - wrapper.style['padding-top'] = '56.25%' + wrapper.style.paddingTop = '56.25%' iframe.style.position = 'absolute' iframe.style.inset = '0' diff --git a/client/src/standalone/player/player.ts b/client/src/standalone/player/player.ts index bbe37a42b..75487258b 100644 --- a/client/src/standalone/player/player.ts +++ b/client/src/standalone/player/player.ts @@ -233,4 +233,4 @@ export class PeerTubePlayer { } // put it on the window as well as the export -(window['PeerTubePlayer'] as any) = PeerTubePlayer +(window as any)['PeerTubePlayer'] = PeerTubePlayer diff --git a/client/src/standalone/videos/embed.ts b/client/src/standalone/videos/embed.ts index d268f4762..cc4274b99 100644 --- a/client/src/standalone/videos/embed.ts +++ b/client/src/standalone/videos/embed.ts @@ -52,7 +52,7 @@ export class PeerTubeEmbed { this.liveManager = new LiveManager(this.playerHTML) try { - this.config = JSON.parse(window['PeerTubeServerConfig']) + this.config = JSON.parse((window as any)['PeerTubeServerConfig']) } catch (err) { logger.error('Cannot parse HTML config.', err) } @@ -254,9 +254,9 @@ export class PeerTubeEmbed { this.player.dispose() this.playerHTML.removePlayerElement() this.playerHTML.displayError('This video is not available because the remote instance is not responding.', translations) - }) + }); - window['videojsPlayer'] = this.player + (window as any)['videojsPlayer'] = this.player this.buildCSS() this.buildPlayerDock(video) diff --git a/client/src/standalone/videos/test-embed.ts b/client/src/standalone/videos/test-embed.ts index ab5262902..b34df11ee 100644 --- a/client/src/standalone/videos/test-embed.ts +++ b/client/src/standalone/videos/test-embed.ts @@ -22,9 +22,9 @@ window.addEventListener('load', async () => { mainElement.appendChild(iframe) logger.info('Document finished loading.') - const player = new PeerTubePlayer(document.querySelector('iframe')) + const player = new PeerTubePlayer(document.querySelector('iframe')); - window['player'] = player + (window as any)['player'] = player logger.info('Awaiting player ready...') await player.ready diff --git a/client/tsconfig.json b/client/tsconfig.json index 1668cfced..785ed1c6c 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -10,7 +10,6 @@ "experimentalDecorators": true, "noImplicitAny": true, "noImplicitThis": true, - "suppressImplicitAnyIndexErrors": true, "alwaysStrict": true, "importHelpers": true, "allowSyntheticDefaultImports": true, diff --git a/shared/core-utils/common/object.ts b/shared/core-utils/common/object.ts index 9780b2594..1276bfcc7 100644 --- a/shared/core-utils/common/object.ts +++ b/shared/core-utils/common/object.ts @@ -23,10 +23,18 @@ function omit (object: O, keys: K[]): Excl return result } +function objectKeysTyped (object: O): K[] { + return (Object.keys(object) as K[]) +} + function getKeys (object: O, keys: K[]): K[] { return (Object.keys(object) as K[]).filter(k => keys.includes(k)) } +function hasKey (obj: T, k: keyof any): k is keyof T { + return k in obj +} + function sortObjectComparator (key: string, order: 'asc' | 'desc') { return (a: any, b: any) => { if (a[key] < b[key]) { @@ -69,7 +77,9 @@ function simpleObjectsDeepEqual (a: any, b: any) { export { pick, omit, + objectKeysTyped, getKeys, + hasKey, shallowCopy, sortObjectComparator, simpleObjectsDeepEqual diff --git a/shared/core-utils/i18n/i18n.ts b/shared/core-utils/i18n/i18n.ts index 38c1b0cc9..54b54077a 100644 --- a/shared/core-utils/i18n/i18n.ts +++ b/shared/core-utils/i18n/i18n.ts @@ -103,9 +103,9 @@ export function is18nLocale (locale: string) { export function getCompleteLocale (locale: string) { if (!locale) return locale - if (I18N_LOCALE_ALIAS[locale]) return I18N_LOCALE_ALIAS[locale] + const found = (I18N_LOCALE_ALIAS as any)[locale] as string - return locale + return found || locale } export function getShortLocale (locale: string) { diff --git a/shared/core-utils/renderer/html.ts b/shared/core-utils/renderer/html.ts index 877f2ec55..365bf7612 100644 --- a/shared/core-utils/renderer/html.ts +++ b/shared/core-utils/renderer/html.ts @@ -56,7 +56,7 @@ export function getCustomMarkupSanitizeOptions (additionalAllowedTags: string[] export function escapeHTML (stringParam: string) { if (!stringParam) return '' - const entityMap = { + const entityMap: { [id: string ]: string } = { '&': '&', '<': '<', '>': '>', diff --git a/shared/models/videos/playlist/video-exist-in-playlist.model.ts b/shared/models/videos/playlist/video-exist-in-playlist.model.ts index bc803a99c..6d06c0f4d 100644 --- a/shared/models/videos/playlist/video-exist-in-playlist.model.ts +++ b/shared/models/videos/playlist/video-exist-in-playlist.model.ts @@ -1,8 +1,8 @@ export type VideosExistInPlaylists = { - [videoId: number ]: VideoExistInPlaylist[] + [videoId: number]: VideoExistInPlaylist[] } export type CachedVideosExistInPlaylists = { - [videoId: number ]: CachedVideoExistInPlaylist[] + [videoId: number]: CachedVideoExistInPlaylist[] } export type CachedVideoExistInPlaylist = {