diff --git a/.eslintrc.js b/.eslintrc.js index f76ca5738d28..d2691c3fbe2b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -95,6 +95,36 @@ const rules = { // Upgrade from a warning '@typescript-eslint/explicit-module-boundary-types': 'error', + + 'no-restricted-syntax': [ + 'error', + { + selector: 'TSInterfaceDeclaration', + message: + 'Prefer `type`. Interfaces are mutable and less powerful, so we prefer `type` for simplicity.', + }, + // Defaults + { + selector: 'ForInStatement', + message: + 'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.', + }, + { + selector: 'ForOfStatement', + message: + 'iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations.', + }, + { + selector: 'LabeledStatement', + message: + 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.', + }, + { + selector: 'WithStatement', + message: + '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.', + }, + ], }; module.exports = { diff --git a/sticker-creator/root.tsx b/sticker-creator/root.tsx index cd35a5a2d85e..ea88c9b690c6 100644 --- a/sticker-creator/root.tsx +++ b/sticker-creator/root.tsx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { hot } from 'react-hot-loader/root'; @@ -11,6 +11,8 @@ import { store } from './store'; import { I18n } from './util/i18n'; declare global { + // We want to extend `window` here. + // eslint-disable-next-line no-restricted-syntax interface Window { localeMessages: { [key: string]: { message: string } }; } diff --git a/sticker-creator/store/ducks/stickers.ts b/sticker-creator/store/ducks/stickers.ts index fa65b0c6cae1..47f670d91019 100644 --- a/sticker-creator/store/ducks/stickers.ts +++ b/sticker-creator/store/ducks/stickers.ts @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable no-param-reassign */ @@ -56,15 +56,15 @@ export const minStickers = 1; export const maxStickers = 200; export const maxByteSize = 300 * 1024; -interface StateStickerData { +type StateStickerData = { readonly imageData?: StickerImageData; readonly emoji?: EmojiPickDataType; -} +}; -interface StateToastData { +type StateToastData = { key: string; subs?: Array; -} +}; export type State = { readonly order: Array; diff --git a/sticker-creator/util/preload.ts b/sticker-creator/util/preload.ts index e6b8be447613..70fe0399cc2f 100644 --- a/sticker-creator/util/preload.ts +++ b/sticker-creator/util/preload.ts @@ -1,9 +1,11 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { Metadata } from 'sharp'; declare global { + // We want to extend `window`'s properties, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface Window { processStickerImage: ProcessStickerImageFn; encryptAndUpload: EncryptAndUploadFn; diff --git a/ts/components/Avatar.tsx b/ts/components/Avatar.tsx index ef5b7023eaf0..c652b24fc2c2 100644 --- a/ts/components/Avatar.tsx +++ b/ts/components/Avatar.tsx @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; @@ -37,10 +37,10 @@ export type Props = { i18n: LocalizerType; } & Pick, 'className'>; -interface State { - imageBroken: boolean; - lastAvatarPath?: string; -} +type State = { + readonly imageBroken: boolean; + readonly lastAvatarPath?: string; +}; export class Avatar extends React.Component { public handleImageErrorBound: () => void; diff --git a/ts/components/CallManager.tsx b/ts/components/CallManager.tsx index 7460853eb5f3..06e5d6f5076c 100644 --- a/ts/components/CallManager.tsx +++ b/ts/components/CallManager.tsx @@ -39,11 +39,11 @@ import { import { LocalizerType } from '../types/Util'; import { missingCaseError } from '../util/missingCaseError'; -interface MeType extends ConversationType { +type MeType = ConversationType & { uuid: string; -} +}; -export interface PropsType { +export type PropsType = { activeCall?: ActiveCallType; availableCameras: Array; cancelCall: (_: CancelCallType) => void; @@ -74,11 +74,11 @@ export interface PropsType { togglePip: () => void; toggleSettings: () => void; toggleSpeakerView: () => void; -} +}; -interface ActiveCallManagerPropsType extends PropsType { +type ActiveCallManagerPropsType = PropsType & { activeCall: ActiveCallType; -} +}; const ActiveCallManager: React.FC = ({ activeCall, diff --git a/ts/components/CallNeedPermissionScreen.tsx b/ts/components/CallNeedPermissionScreen.tsx index 04dca0f9181b..2329b3183cd9 100644 --- a/ts/components/CallNeedPermissionScreen.tsx +++ b/ts/components/CallNeedPermissionScreen.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useRef, useEffect } from 'react'; @@ -8,7 +8,7 @@ import { Intl } from './Intl'; import { ContactName } from './conversation/ContactName'; import { ColorType } from '../types/Colors'; -interface Props { +type Props = { conversation: { avatarPath?: string; color?: ColorType; @@ -19,7 +19,7 @@ interface Props { }; i18n: LocalizerType; close: () => void; -} +}; const AUTO_CLOSE_MS = 10000; diff --git a/ts/components/CallScreen.stories.tsx b/ts/components/CallScreen.stories.tsx index 68fe9cecc58a..da26bce0cc26 100644 --- a/ts/components/CallScreen.stories.tsx +++ b/ts/components/CallScreen.stories.tsx @@ -41,24 +41,24 @@ const conversation = { lastUpdated: Date.now(), }; -interface OverridePropsBase { +type OverridePropsBase = { hasLocalAudio?: boolean; hasLocalVideo?: boolean; isInSpeakerView?: boolean; -} +}; -interface DirectCallOverrideProps extends OverridePropsBase { +type DirectCallOverrideProps = OverridePropsBase & { callMode: CallMode.Direct; callState?: CallState; hasRemoteVideo?: boolean; -} +}; -interface GroupCallOverrideProps extends OverridePropsBase { +type GroupCallOverrideProps = OverridePropsBase & { callMode: CallMode.Group; connectionState?: GroupCallConnectionState; peekedParticipants?: Array; remoteParticipants?: Array; -} +}; const createActiveDirectCallProp = ( overrideProps: DirectCallOverrideProps diff --git a/ts/components/CallingParticipantsList.tsx b/ts/components/CallingParticipantsList.tsx index 3db62907440b..2670bb31c1ac 100644 --- a/ts/components/CallingParticipantsList.tsx +++ b/ts/components/CallingParticipantsList.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable react/no-array-index-key */ @@ -12,10 +12,10 @@ import { LocalizerType } from '../types/Util'; import { sortByTitle } from '../util/sortByTitle'; import { ConversationType } from '../state/ducks/conversations'; -interface ParticipantType extends ConversationType { +type ParticipantType = ConversationType & { hasAudio?: boolean; hasVideo?: boolean; -} +}; export type PropsType = { readonly i18n: LocalizerType; diff --git a/ts/components/CallingPip.tsx b/ts/components/CallingPip.tsx index 2289a610278b..c6a6f1e71998 100644 --- a/ts/components/CallingPip.tsx +++ b/ts/components/CallingPip.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -42,14 +42,14 @@ type PositionState = offsetX: number; }; -interface SnapCandidate { +type SnapCandidate = { mode: | PositionMode.SnapToBottom | PositionMode.SnapToLeft | PositionMode.SnapToRight | PositionMode.SnapToTop; distanceToEdge: number; -} +}; export type PropsType = { activeCall: ActiveCallType; diff --git a/ts/components/CallingPipRemoteVideo.tsx b/ts/components/CallingPipRemoteVideo.tsx index 7d1650d168a1..cd1e8192071e 100644 --- a/ts/components/CallingPipRemoteVideo.tsx +++ b/ts/components/CallingPipRemoteVideo.tsx @@ -61,13 +61,13 @@ const NoVideo = ({ ); }; -export interface PropsType { +export type PropsType = { activeCall: ActiveCallType; getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource; i18n: LocalizerType; setGroupCallVideoRequest: (_: Array) => void; setRendererCanvas: (_: SetRendererCanvasType) => void; -} +}; export const CallingPipRemoteVideo = ({ activeCall, diff --git a/ts/components/CaptionEditor.tsx b/ts/components/CaptionEditor.tsx index ddda08e2c790..4fc4b1c7903a 100644 --- a/ts/components/CaptionEditor.tsx +++ b/ts/components/CaptionEditor.tsx @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -8,18 +8,18 @@ import { AttachmentType } from '../types/Attachment'; import { LocalizerType } from '../types/Util'; -export interface Props { +export type Props = { attachment: AttachmentType; i18n: LocalizerType; url: string; caption?: string; onSave?: (caption: string) => void; close?: () => void; -} +}; -interface State { +type State = { caption: string; -} +}; export class CaptionEditor extends React.Component { private readonly handleKeyDownBound: ( diff --git a/ts/components/CompositionInput.tsx b/ts/components/CompositionInput.tsx index e17e031793da..70b34bf11755 100644 --- a/ts/components/CompositionInput.tsx +++ b/ts/components/CompositionInput.tsx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import * as React from 'react'; @@ -42,20 +42,20 @@ Quill.register('modules/emojiCompletion', EmojiCompletion); Quill.register('modules/mentionCompletion', MentionCompletion); Quill.register('modules/signalClipboard', SignalClipboard); -interface HistoryStatic { +type HistoryStatic = { undo(): void; clear(): void; -} +}; -export interface InputApi { +export type InputApi = { focus: () => void; insertEmoji: (e: EmojiPickDataType) => void; reset: () => void; resetEmojiResults: () => void; submit: () => void; -} +}; -export interface Props { +export type Props = { readonly i18n: LocalizerType; readonly disabled?: boolean; readonly large?: boolean; @@ -75,7 +75,7 @@ export interface Props { onSubmit(message: string, mentions: Array): unknown; getQuotedMessage(): unknown; clearQuotedMessage(): unknown; -} +}; const MAX_LENGTH = 64 * 1024; diff --git a/ts/components/ContactListItem.tsx b/ts/components/ContactListItem.tsx index 416732bc4d45..857bb1f5f54f 100644 --- a/ts/components/ContactListItem.tsx +++ b/ts/components/ContactListItem.tsx @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -11,7 +11,7 @@ import { InContactsIcon } from './InContactsIcon'; import { LocalizerType } from '../types/Util'; import { ColorType } from '../types/Colors'; -interface Props { +type Props = { avatarPath?: string; color?: ColorType; i18n: LocalizerType; @@ -23,7 +23,7 @@ interface Props { phoneNumber?: string; profileName?: string; title: string; -} +}; export class ContactListItem extends React.Component { public renderAvatar(): JSX.Element { diff --git a/ts/components/Countdown.tsx b/ts/components/Countdown.tsx index 1fe331a29bfa..69e02c67217e 100644 --- a/ts/components/Countdown.tsx +++ b/ts/components/Countdown.tsx @@ -1,16 +1,16 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; -export interface Props { +export type Props = { duration: number; expiresAt: number; onComplete?: () => unknown; -} -interface State { +}; +type State = { ratio: number; -} +}; const CIRCUMFERENCE = 11.013 * 2 * Math.PI; diff --git a/ts/components/DirectCallRemoteParticipant.tsx b/ts/components/DirectCallRemoteParticipant.tsx index f89fe611ec50..a0d7cea26683 100644 --- a/ts/components/DirectCallRemoteParticipant.tsx +++ b/ts/components/DirectCallRemoteParticipant.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useRef, useEffect } from 'react'; @@ -8,12 +8,12 @@ import { ColorType } from '../types/Colors'; import { LocalizerType } from '../types/Util'; import { Avatar } from './Avatar'; -interface PropsType { +type PropsType = { conversation: ConversationType; hasRemoteVideo: boolean; i18n: LocalizerType; setRendererCanvas: (_: SetRendererCanvasType) => void; -} +}; export const DirectCallRemoteParticipant: React.FC = ({ conversation, diff --git a/ts/components/ExpiredBuildDialog.tsx b/ts/components/ExpiredBuildDialog.tsx index 728b32ac6431..6ae0698bf608 100644 --- a/ts/components/ExpiredBuildDialog.tsx +++ b/ts/components/ExpiredBuildDialog.tsx @@ -1,14 +1,14 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; import { LocalizerType } from '../types/Util'; -interface PropsType { +type PropsType = { hasExpired: boolean; i18n: LocalizerType; -} +}; export const ExpiredBuildDialog = ({ hasExpired, diff --git a/ts/components/GroupCallOverflowArea.tsx b/ts/components/GroupCallOverflowArea.tsx index e6d5b84f852f..39cac8e838f8 100644 --- a/ts/components/GroupCallOverflowArea.tsx +++ b/ts/components/GroupCallOverflowArea.tsx @@ -16,12 +16,12 @@ const OVERFLOW_SCROLL_BUTTON_RATIO = 0.75; // This should be an integer, as sub-pixel widths can cause performance issues. export const OVERFLOW_PARTICIPANT_WIDTH = 140; -interface PropsType { +type PropsType = { getFrameBuffer: () => ArrayBuffer; getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource; i18n: LocalizerType; overflowedParticipants: ReadonlyArray; -} +}; export const GroupCallOverflowArea: FC = ({ getFrameBuffer, diff --git a/ts/components/GroupCallRemoteParticipant.tsx b/ts/components/GroupCallRemoteParticipant.tsx index c7cd06ea784a..0e15a5055760 100644 --- a/ts/components/GroupCallRemoteParticipant.tsx +++ b/ts/components/GroupCallRemoteParticipant.tsx @@ -24,27 +24,27 @@ import { ContactName } from './conversation/ContactName'; import { useIntersectionObserver } from '../util/hooks'; import { MAX_FRAME_SIZE } from '../calling/constants'; -interface BasePropsType { +type BasePropsType = { getFrameBuffer: () => ArrayBuffer; getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource; i18n: LocalizerType; remoteParticipant: GroupCallRemoteParticipantType; -} +}; -interface InPipPropsType { +type InPipPropsType = { isInPip: true; -} +}; -interface InOverflowAreaPropsType { +type InOverflowAreaPropsType = { height: number; isInPip?: false; width: number; -} +}; -interface InGridPropsType extends InOverflowAreaPropsType { +type InGridPropsType = InOverflowAreaPropsType & { left: number; top: number; -} +}; export type PropsType = BasePropsType & (InPipPropsType | InOverflowAreaPropsType | InGridPropsType); diff --git a/ts/components/GroupCallRemoteParticipants.tsx b/ts/components/GroupCallRemoteParticipants.tsx index 24ebc59ddf79..d56bba2ebed6 100644 --- a/ts/components/GroupCallRemoteParticipants.tsx +++ b/ts/components/GroupCallRemoteParticipants.tsx @@ -25,23 +25,23 @@ const PARTICIPANT_MARGIN = 10; // We scale our video requests down for performance. This number is somewhat arbitrary. const VIDEO_REQUEST_SCALAR = 0.75; -interface Dimensions { +type Dimensions = { width: number; height: number; -} +}; -interface GridArrangement { +type GridArrangement = { rows: Array>; scalar: number; -} +}; -interface PropsType { +type PropsType = { getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource; i18n: LocalizerType; isInSpeakerView: boolean; remoteParticipants: ReadonlyArray; setGroupCallVideoRequest: (_: Array) => void; -} +}; // This component lays out group call remote participants. It uses a custom layout // algorithm (in other words, nothing that the browser provides, like flexbox) in diff --git a/ts/components/GroupCallToastManager.tsx b/ts/components/GroupCallToastManager.tsx index a82267ff071b..bc944af5d313 100644 --- a/ts/components/GroupCallToastManager.tsx +++ b/ts/components/GroupCallToastManager.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { useState, useEffect } from 'react'; @@ -6,10 +6,10 @@ import classNames from 'classnames'; import { GroupCallConnectionState } from '../types/Calling'; import { LocalizerType } from '../types/Util'; -interface PropsType { +type PropsType = { connectionState: GroupCallConnectionState; i18n: LocalizerType; -} +}; // In the future, this component should show toasts when users join or leave. See // DESKTOP-902. diff --git a/ts/components/Intl.tsx b/ts/components/Intl.tsx index 55517f65549d..3bb6d38ebd77 100644 --- a/ts/components/Intl.tsx +++ b/ts/components/Intl.tsx @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -8,13 +8,13 @@ import { ReplacementValuesType } from '../types/I18N'; export type FullJSXType = Array | JSX.Element | string; -export interface Props { +export type Props = { /** The translation string id */ id: string; i18n: LocalizerType; components?: Array | ReplacementValuesType; renderText?: RenderTextCallbackType; -} +}; export class Intl extends React.Component { public static defaultProps: Partial = { diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index d453dabc501d..f7b3d4d008cd 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -17,7 +17,7 @@ import { import { LocalizerType } from '../types/Util'; import { cleanId } from './_util'; -export interface PropsType { +export type PropsType = { conversations?: Array; archivedConversations?: Array; pinnedConversations?: Array; @@ -43,7 +43,7 @@ export interface PropsType { renderNetworkStatus: () => JSX.Element; renderRelinkDialog: () => JSX.Element; renderUpdateDialog: () => JSX.Element; -} +}; // from https://github.com/bvaughn/react-virtualized/blob/fb3484ed5dcc41bffae8eab029126c0fb8f7abc0/source/List/types.js#L5 type RowRendererParamsType = { @@ -69,26 +69,26 @@ export enum HeaderType { Chats, } -interface ArchiveButtonRow { +type ArchiveButtonRow = { type: RowType.ArchiveButton; -} +}; -interface ConversationRow { +type ConversationRow = { index: number; type: | RowType.ArchivedConversation | RowType.Conversation | RowType.PinnedConversation; -} +}; -interface HeaderRow { +type HeaderRow = { headerType: HeaderType; type: RowType.Header; -} +}; -interface UndefinedRow { +type UndefinedRow = { type: RowType.Undefined; -} +}; type Row = ArchiveButtonRow | ConversationRow | HeaderRow | UndefinedRow; diff --git a/ts/components/Lightbox.tsx b/ts/components/Lightbox.tsx index 41bff61ec3c8..d6fc3e4c5645 100644 --- a/ts/components/Lightbox.tsx +++ b/ts/components/Lightbox.tsx @@ -24,7 +24,7 @@ const colorSVG = (url: string, color: string) => { }; }; -export interface Props { +export type Props = { close: () => void; contentType: MIME.MIMEType | undefined; i18n: LocalizerType; @@ -34,10 +34,10 @@ export interface Props { onNext?: () => void; onPrevious?: () => void; onSave?: () => void; -} -interface State { +}; +type State = { videoTime?: number; -} +}; const CONTROLS_WIDTH = 50; const CONTROLS_SPACING = 10; @@ -158,12 +158,12 @@ const styles = { }, }; -interface IconButtonProps { +type IconButtonProps = { i18n: LocalizerType; onClick?: () => void; style?: React.CSSProperties; type: 'save' | 'close' | 'previous' | 'next'; -} +}; const IconButton = ({ i18n, onClick, style, type }: IconButtonProps) => { const clickHandler = (event: React.MouseEvent): void => { diff --git a/ts/components/LightboxGallery.tsx b/ts/components/LightboxGallery.tsx index d13701b51d34..03a1d8b7d235 100644 --- a/ts/components/LightboxGallery.tsx +++ b/ts/components/LightboxGallery.tsx @@ -10,16 +10,16 @@ import { Message } from './conversation/media-gallery/types/Message'; import { AttachmentType } from '../types/Attachment'; import { LocalizerType } from '../types/Util'; -export interface MediaItemType { +export type MediaItemType = { objectURL?: string; thumbnailObjectUrl?: string; contentType?: MIME.MIMEType; index: number; attachment: AttachmentType; message: Message; -} +}; -export interface Props { +export type Props = { close: () => void; i18n: LocalizerType; media: Array; @@ -29,11 +29,11 @@ export interface Props { index: number; }) => void; selectedIndex: number; -} +}; -interface State { +type State = { selectedIndex: number; -} +}; export class LightboxGallery extends React.Component { public static defaultProps: Partial = { diff --git a/ts/components/MainHeader.tsx b/ts/components/MainHeader.tsx index 78d187899171..fdb57856ad7f 100644 --- a/ts/components/MainHeader.tsx +++ b/ts/components/MainHeader.tsx @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -13,7 +13,7 @@ import { AvatarPopup } from './AvatarPopup'; import { LocalizerType } from '../types/Util'; import { ColorType } from '../types/Colors'; -export interface PropsType { +export type PropsType = { searchTerm: string; searchConversationName?: string; searchConversationId?: string; @@ -58,12 +58,12 @@ export interface PropsType { clearSearch: () => void; showArchivedConversations: () => void; -} +}; -interface StateType { +type StateType = { showingAvatarPopup: boolean; popperRoot: HTMLDivElement | null; -} +}; export class MainHeader extends React.Component { private readonly inputRef: React.RefObject; diff --git a/ts/components/MessageBodyHighlight.tsx b/ts/components/MessageBodyHighlight.tsx index ef7879257ead..14a59c14670a 100644 --- a/ts/components/MessageBodyHighlight.tsx +++ b/ts/components/MessageBodyHighlight.tsx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -11,10 +11,10 @@ import { SizeClassType } from './emoji/lib'; import { LocalizerType, RenderTextCallbackType } from '../types/Util'; -export interface Props { +export type Props = { text: string; i18n: LocalizerType; -} +}; const renderNewLines: RenderTextCallbackType = ({ text, key }) => ( diff --git a/ts/components/NetworkStatus.tsx b/ts/components/NetworkStatus.tsx index 170a0cff5269..a0813a092e8a 100644 --- a/ts/components/NetworkStatus.tsx +++ b/ts/components/NetworkStatus.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -8,11 +8,11 @@ import { NetworkStateType } from '../state/ducks/network'; const FIVE_SECONDS = 5 * 1000; -export interface PropsType extends NetworkStateType { +export type PropsType = NetworkStateType & { hasNetworkDialog: boolean; i18n: LocalizerType; manualReconnect: () => void; -} +}; type RenderDialogTypes = { title: string; diff --git a/ts/components/RelinkDialog.tsx b/ts/components/RelinkDialog.tsx index 179ec1a191c2..3005657a07d7 100644 --- a/ts/components/RelinkDialog.tsx +++ b/ts/components/RelinkDialog.tsx @@ -1,15 +1,15 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; import { LocalizerType } from '../types/Util'; -export interface PropsType { +export type PropsType = { i18n: LocalizerType; isRegistrationDone: boolean; relinkDevice: () => void; -} +}; export const RelinkDialog = ({ i18n, diff --git a/ts/components/Spinner.tsx b/ts/components/Spinner.tsx index af6842e63914..d176cd62abdd 100644 --- a/ts/components/Spinner.tsx +++ b/ts/components/Spinner.tsx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -15,11 +15,11 @@ export const SpinnerDirections = [ ] as const; export type SpinnerDirection = typeof SpinnerDirections[number]; -export interface Props { +export type Props = { size?: string; svgSize: SpinnerSvgSize; direction?: SpinnerDirection; -} +}; export const Spinner = ({ size, svgSize, direction }: Props): JSX.Element => (
void; -} +}; export class StartNewConversation extends React.PureComponent { public render(): JSX.Element { diff --git a/ts/components/Tooltip.tsx b/ts/components/Tooltip.tsx index 174416fb4584..6c3f67c5698d 100644 --- a/ts/components/Tooltip.tsx +++ b/ts/components/Tooltip.tsx @@ -7,10 +7,10 @@ import { noop } from 'lodash'; import { Manager, Reference, Popper } from 'react-popper'; import { Theme, themeClassName } from '../util/theme'; -interface EventWrapperPropsType { +type EventWrapperPropsType = { children: React.ReactNode; onHoverChanged: (_: boolean) => void; -} +}; // React doesn't reliably fire `onMouseLeave` or `onMouseOut` events if wrapping a // disabled button. This uses native browser events to avoid that. diff --git a/ts/components/UpdateDialog.tsx b/ts/components/UpdateDialog.tsx index 9ffe2bc87885..ec5ed7f70436 100644 --- a/ts/components/UpdateDialog.tsx +++ b/ts/components/UpdateDialog.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -7,7 +7,7 @@ import { Dialogs } from '../types/Dialogs'; import { Intl } from './Intl'; import { LocalizerType } from '../types/Util'; -export interface PropsType { +export type PropsType = { ackRender: () => void; dialogType: Dialogs; didSnooze: boolean; @@ -17,7 +17,7 @@ export interface PropsType { showEventsCount: number; snoozeUpdate: () => void; startUpdate: () => void; -} +}; export const UpdateDialog = ({ ackRender, diff --git a/ts/components/conversation/AddNewLines.tsx b/ts/components/conversation/AddNewLines.tsx index 6c008cb0fb7c..fd918346710c 100644 --- a/ts/components/conversation/AddNewLines.tsx +++ b/ts/components/conversation/AddNewLines.tsx @@ -5,11 +5,11 @@ import React from 'react'; import { RenderTextCallbackType } from '../../types/Util'; -export interface Props { +export type Props = { text: string; /** Allows you to customize now non-newlines are rendered. Simplest is just a . */ renderNonNewLine?: RenderTextCallbackType; -} +}; export class AddNewLines extends React.Component { public static defaultProps: Partial = { diff --git a/ts/components/conversation/AttachmentList.tsx b/ts/components/conversation/AttachmentList.tsx index 524ee5263ad5..5fe336128702 100644 --- a/ts/components/conversation/AttachmentList.tsx +++ b/ts/components/conversation/AttachmentList.tsx @@ -15,14 +15,14 @@ import { isVideoAttachment, } from '../../types/Attachment'; -export interface Props { +export type Props = { attachments: Array; i18n: LocalizerType; onClickAttachment: (attachment: AttachmentType) => void; onCloseAttachment: (attachment: AttachmentType) => void; onAddAttachment: () => void; onClose: () => void; -} +}; const IMAGE_WIDTH = 120; const IMAGE_HEIGHT = 120; diff --git a/ts/components/conversation/CallingNotification.tsx b/ts/components/conversation/CallingNotification.tsx index 933c8e031cb6..e4ad7eb1667b 100644 --- a/ts/components/conversation/CallingNotification.tsx +++ b/ts/components/conversation/CallingNotification.tsx @@ -14,14 +14,14 @@ import { import { missingCaseError } from '../../util/missingCaseError'; import { Tooltip, TooltipPlacement } from '../Tooltip'; -export interface PropsActionsType { +export type PropsActionsType = { messageSizeChanged: (messageId: string, conversationId: string) => void; returnToActiveCall: () => void; startCallingLobby: (_: { conversationId: string; isVideoCall: boolean; }) => void; -} +}; type PropsHousekeeping = { i18n: LocalizerType; diff --git a/ts/components/conversation/ContactDetail.tsx b/ts/components/conversation/ContactDetail.tsx index 23268a1c7f01..5221279010f2 100644 --- a/ts/components/conversation/ContactDetail.tsx +++ b/ts/components/conversation/ContactDetail.tsx @@ -21,12 +21,12 @@ import { import { LocalizerType } from '../../types/Util'; -export interface Props { +export type Props = { contact: ContactType; hasSignalAccount: boolean; i18n: LocalizerType; onSendMessage: () => void; -} +}; function getLabelForEmail(method: Email, i18n: LocalizerType): string { switch (method.type) { diff --git a/ts/components/conversation/ContactName.tsx b/ts/components/conversation/ContactName.tsx index db3ccd06d251..5ae4b80c0f1a 100644 --- a/ts/components/conversation/ContactName.tsx +++ b/ts/components/conversation/ContactName.tsx @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; @@ -6,14 +6,14 @@ import React from 'react'; import { LocalizerType } from '../../types/Util'; import { Emojify } from './Emojify'; -export interface PropsType { +export type PropsType = { i18n: LocalizerType; title: string; module?: string; name?: string; phoneNumber?: string; profileName?: string; -} +}; export const ContactName = ({ module, title }: PropsType): JSX.Element => { const prefix = module || 'module-contact-name'; diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index 21a1000ed4d5..f7f4bef19916 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -32,7 +32,7 @@ export enum OutgoingCallButtonStyle { Join, } -export interface PropsDataType { +export type PropsDataType = { id: string; name?: string; @@ -58,9 +58,9 @@ export interface PropsDataType { showBackButton?: boolean; outgoingCallButtonStyle: OutgoingCallButtonStyle; -} +}; -export interface PropsActionsType { +export type PropsActionsType = { onSetMuteNotifications: (seconds: number) => void; onSetDisappearingMessages: (seconds: number) => void; onDeleteMessages: () => void; @@ -78,11 +78,11 @@ export interface PropsActionsType { onArchive: () => void; onMarkUnread: () => void; onMoveToInbox: () => void; -} +}; -export interface PropsHousekeepingType { +export type PropsHousekeepingType = { i18n: LocalizerType; -} +}; export type PropsType = PropsDataType & PropsActionsType & diff --git a/ts/components/conversation/EmbeddedContact.tsx b/ts/components/conversation/EmbeddedContact.tsx index 2ec8ab66c5c7..2d2ab4b196ae 100644 --- a/ts/components/conversation/EmbeddedContact.tsx +++ b/ts/components/conversation/EmbeddedContact.tsx @@ -13,7 +13,7 @@ import { renderName, } from './_contactUtil'; -export interface Props { +export type Props = { contact: ContactType; i18n: LocalizerType; isIncoming: boolean; @@ -21,7 +21,7 @@ export interface Props { withContentBelow: boolean; tabIndex: number; onClick?: () => void; -} +}; export class EmbeddedContact extends React.Component { public render(): JSX.Element { diff --git a/ts/components/conversation/Emojify.tsx b/ts/components/conversation/Emojify.tsx index 8a28c2544b4e..255b9f5a1b15 100644 --- a/ts/components/conversation/Emojify.tsx +++ b/ts/components/conversation/Emojify.tsx @@ -40,13 +40,13 @@ function getImageTag({ ); } -export interface Props { +export type Props = { text: string; /** A class name to be added to the generated emoji images */ sizeClass?: SizeClassType; /** Allows you to customize now non-newlines are rendered. Simplest is just a . */ renderNonEmoji?: RenderTextCallbackType; -} +}; export class Emojify extends React.Component { public static defaultProps: Partial = { diff --git a/ts/components/conversation/ExpireTimer.tsx b/ts/components/conversation/ExpireTimer.tsx index d0384597255a..1e526100d57c 100644 --- a/ts/components/conversation/ExpireTimer.tsx +++ b/ts/components/conversation/ExpireTimer.tsx @@ -6,14 +6,14 @@ import classNames from 'classnames'; import { getIncrement, getTimerBucket } from '../../util/timer'; -export interface Props { +export type Props = { withImageNoCaption?: boolean; withSticker?: boolean; withTapToViewExpired?: boolean; expirationLength: number; expirationTimestamp: number; direction?: 'incoming' | 'outgoing'; -} +}; export class ExpireTimer extends React.Component { private interval: NodeJS.Timeout | null; diff --git a/ts/components/conversation/GroupNotification.tsx b/ts/components/conversation/GroupNotification.tsx index 1202cd58acb7..41f1c8438d0b 100644 --- a/ts/components/conversation/GroupNotification.tsx +++ b/ts/components/conversation/GroupNotification.tsx @@ -10,21 +10,21 @@ import { LocalizerType } from '../../types/Util'; import { missingCaseError } from '../../util/missingCaseError'; -interface Contact { +type Contact = { phoneNumber?: string; profileName?: string; name?: string; title: string; isMe?: boolean; -} +}; export type ChangeType = 'add' | 'remove' | 'name' | 'avatar' | 'general'; -interface Change { +type Change = { type: ChangeType; newName?: string; contacts?: Array; -} +}; export type PropsData = { from: Contact; diff --git a/ts/components/conversation/Image.tsx b/ts/components/conversation/Image.tsx index a423ac9f8d62..a2ed8ec060fd 100644 --- a/ts/components/conversation/Image.tsx +++ b/ts/components/conversation/Image.tsx @@ -9,7 +9,7 @@ import { Spinner } from '../Spinner'; import { LocalizerType } from '../../types/Util'; import { AttachmentType } from '../../types/Attachment'; -export interface Props { +export type Props = { alt: string; attachment: AttachmentType; url: string; @@ -40,7 +40,7 @@ export interface Props { onClick?: (attachment: AttachmentType) => void; onClickClose?: (attachment: AttachmentType) => void; onError?: () => void; -} +}; export class Image extends React.Component { private canClick() { diff --git a/ts/components/conversation/ImageGrid.tsx b/ts/components/conversation/ImageGrid.tsx index 40ce704b058f..23c65a1c964f 100644 --- a/ts/components/conversation/ImageGrid.tsx +++ b/ts/components/conversation/ImageGrid.tsx @@ -18,7 +18,7 @@ import { Image } from './Image'; import { LocalizerType } from '../../types/Util'; -export interface Props { +export type Props = { attachments: Array; withContentAbove?: boolean; withContentBelow?: boolean; @@ -31,7 +31,7 @@ export interface Props { onError: () => void; onClick?: (attachment: AttachmentType) => void; -} +}; export const ImageGrid = ({ attachments, diff --git a/ts/components/conversation/LinkPreviewDate.tsx b/ts/components/conversation/LinkPreviewDate.tsx index 377d42dd479b..1d672166a514 100644 --- a/ts/components/conversation/LinkPreviewDate.tsx +++ b/ts/components/conversation/LinkPreviewDate.tsx @@ -5,10 +5,10 @@ import * as React from 'react'; import moment, { Moment } from 'moment'; import { isLinkPreviewDateValid } from '../../linkPreviews/isLinkPreviewDateValid'; -interface Props { +type Props = { date: null | number; className?: string; -} +}; export const LinkPreviewDate: React.FC = ({ date, diff --git a/ts/components/conversation/Linkify.tsx b/ts/components/conversation/Linkify.tsx index ba5fb84a9dea..c5f4f55855f5 100644 --- a/ts/components/conversation/Linkify.tsx +++ b/ts/components/conversation/Linkify.tsx @@ -36,11 +36,11 @@ const linkify = LinkifyIt() 'travel', ]); -export interface Props { +export type Props = { text: string; /** Allows you to customize now non-links are rendered. Simplest is just a . */ renderNonLink?: RenderTextCallbackType; -} +}; const SUPPORTED_PROTOCOLS = /^(http|https):/i; diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 84f605077905..c406b285162c 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -52,9 +52,9 @@ import { createRefMerger } from '../_util'; import { emojiToData } from '../emoji/lib'; import { SmartReactionPicker } from '../../state/smart/ReactionPicker'; -interface Trigger { +type Trigger = { handleContextClick: (event: React.MouseEvent) => void; -} +}; const STICKER_SIZE = 200; const SELECTED_TIMEOUT = 1000; @@ -190,7 +190,7 @@ export type Props = PropsData & PropsActions & Pick; -interface State { +type State = { expiring: boolean; expired: boolean; imageBroken: boolean; @@ -205,7 +205,7 @@ interface State { containerWidth: number; canDeleteForEveryone: boolean; -} +}; const EXPIRATION_CHECK_MINIMUM = 2000; const EXPIRED_DELAY = 600; diff --git a/ts/components/conversation/MessageBody.tsx b/ts/components/conversation/MessageBody.tsx index cdf47ccee1d9..47bca72826db 100644 --- a/ts/components/conversation/MessageBody.tsx +++ b/ts/components/conversation/MessageBody.tsx @@ -20,7 +20,7 @@ type OpenConversationActionType = ( messageId?: string ) => void; -export interface Props { +export type Props = { direction?: 'incoming' | 'outgoing'; text: string; textPending?: boolean; @@ -31,7 +31,7 @@ export interface Props { i18n: LocalizerType; bodyRanges?: BodyRangesType; openConversation?: OpenConversationActionType; -} +}; const renderEmoji = ({ text, diff --git a/ts/components/conversation/MessageDetail.tsx b/ts/components/conversation/MessageDetail.tsx index 64c478cfc72f..a768e0ee665a 100644 --- a/ts/components/conversation/MessageDetail.tsx +++ b/ts/components/conversation/MessageDetail.tsx @@ -11,7 +11,7 @@ import { Message, MessageStatusType, Props as MessageProps } from './Message'; import { LocalizerType } from '../../types/Util'; import { ColorType } from '../../types/Colors'; -interface Contact { +type Contact = { status: MessageStatusType; title: string; @@ -27,9 +27,9 @@ interface Contact { onSendAnyway: () => void; onShowSafetyNumber: () => void; -} +}; -export interface Props { +export type Props = { sentAt: number; receivedAt: number; @@ -38,7 +38,7 @@ export interface Props { contacts: Array; i18n: LocalizerType; -} +}; const _keyForError = (error: Error): string => { return `${error.name}-${error.message}`; diff --git a/ts/components/conversation/ProfileChangeNotification.tsx b/ts/components/conversation/ProfileChangeNotification.tsx index f2e8ccd4c6bf..a023d577ac08 100644 --- a/ts/components/conversation/ProfileChangeNotification.tsx +++ b/ts/components/conversation/ProfileChangeNotification.tsx @@ -11,11 +11,11 @@ import { ProfileNameChangeType, } from '../../util/getStringForProfileChange'; -export interface PropsType { +export type PropsType = { change: ProfileNameChangeType; changedContact: ConversationType; i18n: LocalizerType; -} +}; export function ProfileChangeNotification(props: PropsType): JSX.Element { const { change, changedContact, i18n } = props; diff --git a/ts/components/conversation/Quote.tsx b/ts/components/conversation/Quote.tsx index 2502e94a46d3..254da42b7786 100644 --- a/ts/components/conversation/Quote.tsx +++ b/ts/components/conversation/Quote.tsx @@ -13,7 +13,7 @@ import { ColorType } from '../../types/Colors'; import { ContactName } from './ContactName'; import { getTextWithMentions } from '../../util/getTextWithMentions'; -export interface Props { +export type Props = { attachment?: QuotedAttachmentType; authorTitle: string; authorPhoneNumber?: string; @@ -29,25 +29,25 @@ export interface Props { onClose?: () => void; text: string; referencedMessageNotFound: boolean; -} +}; -interface State { +type State = { imageBroken: boolean; -} +}; -export interface QuotedAttachmentType { +export type QuotedAttachmentType = { contentType: MIME.MIMEType; fileName: string; /** Not included in protobuf */ isVoiceMessage: boolean; thumbnail?: Attachment; -} +}; -interface Attachment { +type Attachment = { contentType: MIME.MIMEType; /** Not included in protobuf, and is loaded asynchronously */ objectUrl?: string; -} +}; function validateQuote(quote: Props): boolean { if (quote.text) { diff --git a/ts/components/conversation/ReactionViewer.tsx b/ts/components/conversation/ReactionViewer.tsx index ecc37f650f19..7f18b105c338 100644 --- a/ts/components/conversation/ReactionViewer.tsx +++ b/ts/components/conversation/ReactionViewer.tsx @@ -46,12 +46,12 @@ const DEFAULT_EMOJI_ORDER = [ 'rage', ]; -interface ReactionCategory { +type ReactionCategory = { count: number; emoji?: string; id: string; index: number; -} +}; type ReactionWithEmojiData = Reaction & EmojiData; diff --git a/ts/components/conversation/ResetSessionNotification.tsx b/ts/components/conversation/ResetSessionNotification.tsx index 064d38be7c94..18fad2304afc 100644 --- a/ts/components/conversation/ResetSessionNotification.tsx +++ b/ts/components/conversation/ResetSessionNotification.tsx @@ -5,9 +5,9 @@ import React from 'react'; import { LocalizerType } from '../../types/Util'; -export interface Props { +export type Props = { i18n: LocalizerType; -} +}; export const ResetSessionNotification = ({ i18n }: Props): JSX.Element => (
diff --git a/ts/components/conversation/SafetyNumberNotification.tsx b/ts/components/conversation/SafetyNumberNotification.tsx index b2d14a68c959..2220bc0135fd 100644 --- a/ts/components/conversation/SafetyNumberNotification.tsx +++ b/ts/components/conversation/SafetyNumberNotification.tsx @@ -7,13 +7,13 @@ import { ContactName } from './ContactName'; import { Intl } from '../Intl'; import { LocalizerType } from '../../types/Util'; -export interface ContactType { +export type ContactType = { id: string; phoneNumber?: string; profileName?: string; title: string; name?: string; -} +}; export type PropsData = { isGroup: boolean; diff --git a/ts/components/conversation/StagedGenericAttachment.tsx b/ts/components/conversation/StagedGenericAttachment.tsx index 4fa47530c2bf..8c3ef18fa7fc 100644 --- a/ts/components/conversation/StagedGenericAttachment.tsx +++ b/ts/components/conversation/StagedGenericAttachment.tsx @@ -6,11 +6,11 @@ import React from 'react'; import { AttachmentType, getExtensionForDisplay } from '../../types/Attachment'; import { LocalizerType } from '../../types/Util'; -export interface Props { +export type Props = { attachment: AttachmentType; onClose: (attachment: AttachmentType) => void; i18n: LocalizerType; -} +}; export const StagedGenericAttachment = ({ attachment, diff --git a/ts/components/conversation/StagedLinkPreview.tsx b/ts/components/conversation/StagedLinkPreview.tsx index c31bba3eb207..374a1309570e 100644 --- a/ts/components/conversation/StagedLinkPreview.tsx +++ b/ts/components/conversation/StagedLinkPreview.tsx @@ -10,7 +10,7 @@ import { LinkPreviewDate } from './LinkPreviewDate'; import { AttachmentType, isImageAttachment } from '../../types/Attachment'; import { LocalizerType } from '../../types/Util'; -export interface Props { +export type Props = { isLoaded: boolean; title: string; description: null | string; @@ -20,7 +20,7 @@ export interface Props { i18n: LocalizerType; onClose?: () => void; -} +}; export const StagedLinkPreview: React.FC = ({ isLoaded, diff --git a/ts/components/conversation/StagedPlaceholderAttachment.tsx b/ts/components/conversation/StagedPlaceholderAttachment.tsx index 34e067469627..911929af7422 100644 --- a/ts/components/conversation/StagedPlaceholderAttachment.tsx +++ b/ts/components/conversation/StagedPlaceholderAttachment.tsx @@ -1,13 +1,13 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; import { LocalizerType } from '../../types/Util'; -interface Props { +type Props = { onClick: () => void; i18n: LocalizerType; -} +}; export const StagedPlaceholderAttachment = ({ i18n, diff --git a/ts/components/conversation/Timestamp.tsx b/ts/components/conversation/Timestamp.tsx index 9ea5524393e1..0d0ad3addb6c 100644 --- a/ts/components/conversation/Timestamp.tsx +++ b/ts/components/conversation/Timestamp.tsx @@ -9,7 +9,7 @@ import { formatRelativeTime } from '../../util/formatRelativeTime'; import { LocalizerType } from '../../types/Util'; -export interface Props { +export type Props = { timestamp?: number; extended?: boolean; module?: string; @@ -19,7 +19,7 @@ export interface Props { withUnread?: boolean; direction?: 'incoming' | 'outgoing'; i18n: LocalizerType; -} +}; const UPDATE_FREQUENCY = 60 * 1000; diff --git a/ts/components/conversation/TypingAnimation.tsx b/ts/components/conversation/TypingAnimation.tsx index efa0682d1104..383cda6f4721 100644 --- a/ts/components/conversation/TypingAnimation.tsx +++ b/ts/components/conversation/TypingAnimation.tsx @@ -6,10 +6,10 @@ import classNames from 'classnames'; import { LocalizerType } from '../../types/Util'; -export interface Props { +export type Props = { i18n: LocalizerType; color?: string; -} +}; export const TypingAnimation = ({ i18n, color }: Props): JSX.Element => (
diff --git a/ts/components/conversation/TypingBubble.tsx b/ts/components/conversation/TypingBubble.tsx index ce62179ba185..32c636464b32 100644 --- a/ts/components/conversation/TypingBubble.tsx +++ b/ts/components/conversation/TypingBubble.tsx @@ -10,7 +10,7 @@ import { Avatar } from '../Avatar'; import { LocalizerType } from '../../types/Util'; import { ColorType } from '../../types/Colors'; -export interface Props { +export type Props = { avatarPath?: string; color: ColorType; name?: string; @@ -19,7 +19,7 @@ export interface Props { title: string; conversationType: 'group' | 'direct'; i18n: LocalizerType; -} +}; export class TypingBubble extends React.PureComponent { public renderAvatar(): JSX.Element | null { diff --git a/ts/components/conversation/UnsupportedMessage.tsx b/ts/components/conversation/UnsupportedMessage.tsx index c1487f68aaa8..54355c4b60ec 100644 --- a/ts/components/conversation/UnsupportedMessage.tsx +++ b/ts/components/conversation/UnsupportedMessage.tsx @@ -8,14 +8,14 @@ import { ContactName } from './ContactName'; import { Intl } from '../Intl'; import { LocalizerType } from '../../types/Util'; -export interface ContactType { +export type ContactType = { id: string; phoneNumber?: string; profileName?: string; title: string; name?: string; isMe: boolean; -} +}; export type PropsData = { canProcessNow: boolean; diff --git a/ts/components/conversation/VerificationNotification.tsx b/ts/components/conversation/VerificationNotification.tsx index e25376261cde..d06a08ccee6d 100644 --- a/ts/components/conversation/VerificationNotification.tsx +++ b/ts/components/conversation/VerificationNotification.tsx @@ -10,12 +10,12 @@ import { LocalizerType } from '../../types/Util'; import { missingCaseError } from '../../util/missingCaseError'; -interface Contact { +type Contact = { phoneNumber?: string; profileName?: string; name?: string; title: string; -} +}; export type PropsData = { type: 'markVerified' | 'markNotVerified'; diff --git a/ts/components/conversation/media-gallery/AttachmentSection.tsx b/ts/components/conversation/media-gallery/AttachmentSection.tsx index 0c29320b9980..662cc01e33b4 100644 --- a/ts/components/conversation/media-gallery/AttachmentSection.tsx +++ b/ts/components/conversation/media-gallery/AttachmentSection.tsx @@ -10,13 +10,13 @@ import { MediaItemType } from '../../LightboxGallery'; import { missingCaseError } from '../../../util/missingCaseError'; import { LocalizerType } from '../../../types/Util'; -export interface Props { +export type Props = { i18n: LocalizerType; header?: string; type: 'media' | 'documents'; mediaItems: Array; onItemClick?: (event: ItemClickEvent) => void; -} +}; export class AttachmentSection extends React.Component { public render(): JSX.Element { diff --git a/ts/components/conversation/media-gallery/DocumentListItem.tsx b/ts/components/conversation/media-gallery/DocumentListItem.tsx index f71734898c9a..ef2cfa884534 100644 --- a/ts/components/conversation/media-gallery/DocumentListItem.tsx +++ b/ts/components/conversation/media-gallery/DocumentListItem.tsx @@ -7,7 +7,7 @@ import classNames from 'classnames'; import moment from 'moment'; import formatFileSize from 'filesize'; -interface Props { +type Props = { // Required timestamp: number; @@ -16,7 +16,7 @@ interface Props { fileSize?: number; onClick?: () => void; shouldShowSeparator?: boolean; -} +}; export class DocumentListItem extends React.Component { public static defaultProps: Partial = { diff --git a/ts/components/conversation/media-gallery/EmptyState.tsx b/ts/components/conversation/media-gallery/EmptyState.tsx index ea9792875d4c..4ec96701704d 100644 --- a/ts/components/conversation/media-gallery/EmptyState.tsx +++ b/ts/components/conversation/media-gallery/EmptyState.tsx @@ -3,9 +3,9 @@ import React from 'react'; -interface Props { +type Props = { label: string; -} +}; export const EmptyState = ({ label }: Props): JSX.Element => (
{label}
diff --git a/ts/components/conversation/media-gallery/MediaGallery.tsx b/ts/components/conversation/media-gallery/MediaGallery.tsx index 289b74e1d2e8..38a6b805d0c5 100644 --- a/ts/components/conversation/media-gallery/MediaGallery.tsx +++ b/ts/components/conversation/media-gallery/MediaGallery.tsx @@ -15,23 +15,23 @@ import { LocalizerType } from '../../../types/Util'; import { MediaItemType } from '../../LightboxGallery'; -export interface Props { +export type Props = { documents: Array; i18n: LocalizerType; media: Array; onItemClick?: (event: ItemClickEvent) => void; -} +}; -interface State { +type State = { selectedTab: 'media' | 'documents'; -} +}; const MONTH_FORMAT = 'MMMM YYYY'; -interface TabSelectEvent { +type TabSelectEvent = { type: 'media' | 'documents'; -} +}; const Tab = ({ isSelected, diff --git a/ts/components/conversation/media-gallery/MediaGridItem.tsx b/ts/components/conversation/media-gallery/MediaGridItem.tsx index fe13fd1bfd42..59f97a9eadbc 100644 --- a/ts/components/conversation/media-gallery/MediaGridItem.tsx +++ b/ts/components/conversation/media-gallery/MediaGridItem.tsx @@ -11,15 +11,15 @@ import { import { LocalizerType } from '../../../types/Util'; import { MediaItemType } from '../../LightboxGallery'; -export interface Props { +export type Props = { mediaItem: MediaItemType; onClick?: () => void; i18n: LocalizerType; -} +}; -interface State { +type State = { imageBroken: boolean; -} +}; export class MediaGridItem extends React.Component { private readonly onImageErrorBound: () => void; diff --git a/ts/components/conversation/media-gallery/groupMediaItemsByDate.ts b/ts/components/conversation/media-gallery/groupMediaItemsByDate.ts index 22121e8b7dd8..89dd4411163c 100644 --- a/ts/components/conversation/media-gallery/groupMediaItemsByDate.ts +++ b/ts/components/conversation/media-gallery/groupMediaItemsByDate.ts @@ -11,10 +11,10 @@ import { MediaItemType } from '../../LightboxGallery'; type StaticSectionType = 'today' | 'yesterday' | 'thisWeek' | 'thisMonth'; type YearMonthSectionType = 'yearMonth'; -interface GenericSection { +type GenericSection = { type: T; mediaItems: Array; -} +}; type StaticSection = GenericSection; type YearMonthSection = GenericSection & { year: number; @@ -93,11 +93,11 @@ const toSection = ( } }; -interface GenericMediaItemWithSection { +type GenericMediaItemWithSection = { order: number; type: T; mediaItem: MediaItemType; -} +}; type MediaItemWithStaticSection = GenericMediaItemWithSection< StaticSectionType >; diff --git a/ts/components/conversation/media-gallery/types/ItemClickEvent.ts b/ts/components/conversation/media-gallery/types/ItemClickEvent.ts index 29420a6bf00c..2eeec892fe38 100644 --- a/ts/components/conversation/media-gallery/types/ItemClickEvent.ts +++ b/ts/components/conversation/media-gallery/types/ItemClickEvent.ts @@ -1,11 +1,11 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { AttachmentType } from '../../../../types/Attachment'; import { Message } from './Message'; -export interface ItemClickEvent { +export type ItemClickEvent = { message: Message; attachment: AttachmentType; type: 'media' | 'documents'; -} +}; diff --git a/ts/linkPreviews/linkPreviewFetch.ts b/ts/linkPreviews/linkPreviewFetch.ts index 869ac0730669..302026dbabf2 100644 --- a/ts/linkPreviews/linkPreviewFetch.ts +++ b/ts/linkPreviews/linkPreviewFetch.ts @@ -54,17 +54,17 @@ const emptyContentType = { type: null, charset: null }; type FetchFn = (href: string, init: RequestInit) => Promise; -export interface LinkPreviewMetadata { +export type LinkPreviewMetadata = { title: string; description: null | string; date: null | number; imageHref: null | string; -} +}; -export interface LinkPreviewImage { +export type LinkPreviewImage = { data: ArrayBuffer; contentType: MIMEType; -} +}; type ParsedContentType = | { type: null; charset: null } diff --git a/ts/model-types.d.ts b/ts/model-types.d.ts index 7bd3240a66e3..03e7c37702d8 100644 --- a/ts/model-types.d.ts +++ b/ts/model-types.d.ts @@ -41,10 +41,10 @@ export declare class DeletesModelType extends Backbone.Model< type TaskResultType = any; -export interface CustomError extends Error { +export type CustomError = Error & { identifier?: string; number?: string; -} +}; export type GroupMigrationType = { areWeInvited: boolean; diff --git a/ts/models/conversations.ts b/ts/models/conversations.ts index a0e3e8ec00cc..ba2496a84abd 100644 --- a/ts/models/conversations.ts +++ b/ts/models/conversations.ts @@ -76,10 +76,10 @@ const COLORS = [ const THREE_HOURS = 3 * 60 * 60 * 1000; -interface CustomError extends Error { +type CustomError = Error & { identifier?: string; number?: string; -} +}; export class ConversationModel extends window.Backbone.Model< ConversationAttributesType @@ -4556,9 +4556,9 @@ window.Whisper.GroupMemberConversation = window.Backbone.Model.extend({ }, }); -interface SortableByTitle { +type SortableByTitle = { getTitle: () => string; -} +}; const sortConversationTitles = ( left: SortableByTitle, diff --git a/ts/notifications/getStatus.ts b/ts/notifications/getStatus.ts index 3085004a11eb..262ca6cce7a5 100644 --- a/ts/notifications/getStatus.ts +++ b/ts/notifications/getStatus.ts @@ -1,20 +1,20 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -interface Environment { +type Environment = { isAppFocused: boolean; isAudioNotificationEnabled: boolean; isEnabled: boolean; hasNotifications: boolean; userSetting: UserSetting; -} +}; -interface Status { +type Status = { shouldClearNotifications: boolean; shouldPlayNotificationSound: boolean; shouldShowNotifications: boolean; type: Type; -} +}; type UserSetting = 'off' | 'count' | 'name' | 'message'; diff --git a/ts/quill/emoji/completion.tsx b/ts/quill/emoji/completion.tsx index 28e4dfd7aa8f..3cc2413df254 100644 --- a/ts/quill/emoji/completion.tsx +++ b/ts/quill/emoji/completion.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import Quill from 'quill'; @@ -22,11 +22,11 @@ import { getBlotTextPartitions, matchBlotTextPartitions } from '../util'; const Keyboard = Quill.import('modules/keyboard'); -interface EmojiPickerOptions { +type EmojiPickerOptions = { onPickEmoji: (emoji: EmojiPickDataType) => void; setEmojiPickerElement: (element: JSX.Element | null) => void; skinTone: number; -} +}; export class EmojiCompletion { results: Array; diff --git a/ts/quill/mentions/completion.tsx b/ts/quill/mentions/completion.tsx index 3f22feb0f45a..1bf59e43b2d8 100644 --- a/ts/quill/mentions/completion.tsx +++ b/ts/quill/mentions/completion.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import _ from 'lodash'; @@ -15,14 +15,16 @@ import { LocalizerType } from '../../types/Util'; import { MemberRepository } from '../memberRepository'; import { matchBlotTextPartitions } from '../util'; -export interface MentionCompletionOptions { +export type MentionCompletionOptions = { i18n: LocalizerType; memberRepositoryRef: RefObject; setMentionPickerElement: (element: JSX.Element | null) => void; me?: ConversationType; -} +}; declare global { + // We want to extend `HTMLElement`'s properties, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface HTMLElement { // Webkit-specific scrollIntoViewIfNeeded: (bringToCenter: boolean) => void; diff --git a/ts/quill/types.d.ts b/ts/quill/types.d.ts index 564b4c4b4777..4ce6c33f7e87 100644 --- a/ts/quill/types.d.ts +++ b/ts/quill/types.d.ts @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import UpdatedDelta from 'quill-delta'; @@ -11,6 +11,8 @@ declare module 'react-quill' { type DeltaStatic = UpdatedDelta; } +// We want to extend some existing interfaces. +/* eslint-disable no-restricted-syntax */ declare module 'quill' { // this type is fixed in @types/quill, but our version of react-quill cannot // use the version of quill that has this fix in its typings @@ -76,3 +78,4 @@ declare module 'quill' { bindings: Record>; } } +/* eslint-enable no-restricted-syntax */ diff --git a/ts/quill/util.ts b/ts/quill/util.ts index dbf73d1d8d23..ae80b7c06c50 100644 --- a/ts/quill/util.ts +++ b/ts/quill/util.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import emojiRegex from 'emoji-regex'; @@ -9,10 +9,10 @@ import Op from 'quill-delta/dist/Op'; import { BodyRangeType } from '../types/Util'; import { MentionBlot } from './mentions/blot'; -export interface MentionBlotValue { +export type MentionBlotValue = { uuid: string; title: string; -} +}; export const isMentionBlot = (blot: LeafBlot): blot is MentionBlot => blot.value() && blot.value().mention; diff --git a/ts/sql/Interface.ts b/ts/sql/Interface.ts index 83b2e0039412..7bb347bafa0a 100644 --- a/ts/sql/Interface.ts +++ b/ts/sql/Interface.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable @typescript-eslint/ban-types */ @@ -30,7 +30,7 @@ export type StickerPackType = any; export type StickerType = any; export type UnprocessedType = any; -export interface DataInterface { +export type DataInterface = { close: () => Promise; removeDB: () => Promise; removeIndexedDBFiles: () => Promise; @@ -188,7 +188,7 @@ export interface DataInterface { conversationId: string, options: { limit: number } ) => Promise>; -} +}; // The reason for client/server divergence is the need to inject Backbone models and // collections into data calls so those are the objects returned. This was necessary in diff --git a/ts/sql/Server.ts b/ts/sql/Server.ts index 0b54258e8f4a..8ccc1a4d0147 100644 --- a/ts/sql/Server.ts +++ b/ts/sql/Server.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable no-nested-ternary */ @@ -56,6 +56,8 @@ import { } from './Interface'; declare global { + // We want to extend `Function`'s properties, so we need to use an interface. + // eslint-disable-next-line no-restricted-syntax interface Function { needsSerial?: boolean; } diff --git a/ts/sqlcipher.d.ts b/ts/sqlcipher.d.ts index 5d21c35a13f3..0ddcb43b0406 100644 --- a/ts/sqlcipher.d.ts +++ b/ts/sqlcipher.d.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // Taken from: @@ -35,10 +35,10 @@ declare module '@journeyapps/sqlcipher' { ): Database; }; - export interface RunResult extends Statement { + export type RunResult = Statement & { lastID: number; changes: number; - } + }; export class Statement { bind(callback?: (err: Error | null) => void): this; diff --git a/ts/state/createStore.ts b/ts/state/createStore.ts index 297950e63824..81ff8fc70dbc 100644 --- a/ts/state/createStore.ts +++ b/ts/state/createStore.ts @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable no-console */ @@ -17,7 +17,8 @@ import { createLogger } from 'redux-logger'; import { reducer, StateType } from './reducer'; declare global { - // eslint-disable-next-line @typescript-eslint/no-unused-vars + // We want to extend `window`'s properties, so we need an interface. + // eslint-disable-next-line no-restricted-syntax, @typescript-eslint/no-unused-vars interface Console { _log: Console['log']; } diff --git a/ts/state/ducks/calling.ts b/ts/state/ducks/calling.ts index cca5b5912698..75e76b0f008c 100644 --- a/ts/state/ducks/calling.ts +++ b/ts/state/ducks/calling.ts @@ -30,24 +30,24 @@ import { LatestQueue } from '../../util/LatestQueue'; // State -export interface GroupCallPeekInfoType { +export type GroupCallPeekInfoType = { uuids: Array; creatorUuid?: string; eraId?: string; maxDevices: number; deviceCount: number; -} +}; -export interface GroupCallParticipantInfoType { +export type GroupCallParticipantInfoType = { uuid: string; demuxId: number; hasRemoteAudio: boolean; hasRemoteVideo: boolean; speakerTime?: number; videoAspectRatio: number; -} +}; -export interface DirectCallStateType { +export type DirectCallStateType = { callMode: CallMode.Direct; conversationId: string; callState?: CallState; @@ -55,18 +55,18 @@ export interface DirectCallStateType { isIncoming: boolean; isVideoCall: boolean; hasRemoteVideo?: boolean; -} +}; -export interface GroupCallStateType { +export type GroupCallStateType = { callMode: CallMode.Group; conversationId: string; connectionState: GroupCallConnectionState; joinState: GroupCallJoinState; peekInfo: GroupCallPeekInfoType; remoteParticipants: Array; -} +}; -export interface ActiveCallStateType { +export type ActiveCallStateType = { conversationId: string; hasLocalAudio: boolean; hasLocalVideo: boolean; @@ -76,11 +76,11 @@ export interface ActiveCallStateType { safetyNumberChangedUuids: Array; settingsDialogOpen: boolean; showParticipantsList: boolean; -} +}; -export interface CallsByConversationType { +export type CallsByConversationType = { [conversationId: string]: DirectCallStateType | GroupCallStateType; -} +}; export type CallingStateType = MediaDeviceSettings & { callsByConversation: CallsByConversationType; @@ -145,15 +145,15 @@ type PeekNotConnectedGroupCallType = { conversationId: string; }; -interface StartDirectCallType { +type StartDirectCallType = { conversationId: string; hasLocalAudio: boolean; hasLocalVideo: boolean; -} +}; -export interface StartCallType extends StartDirectCallType { +export type StartCallType = StartDirectCallType & { callMode: CallMode.Direct | CallMode.Group; -} +}; export type RemoteVideoChangeType = { conversationId: string; diff --git a/ts/state/smart/ConversationHeader.tsx b/ts/state/smart/ConversationHeader.tsx index 0141c274f71e..6cf642238f38 100644 --- a/ts/state/smart/ConversationHeader.tsx +++ b/ts/state/smart/ConversationHeader.tsx @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { connect } from 'react-redux'; @@ -20,7 +20,7 @@ import { getOwn } from '../../util/getOwn'; import { missingCaseError } from '../../util/missingCaseError'; import { isGroupCallingEnabled } from '../../util/isGroupCallingEnabled'; -export interface OwnProps { +export type OwnProps = { id: string; onDeleteMessages: () => void; @@ -39,7 +39,7 @@ export interface OwnProps { onMarkUnread: () => void; onMoveToInbox: () => void; onShowSafetyNumber: () => void; -} +}; const getOutgoingCallButtonStyle = ( conversation: ConversationType, diff --git a/ts/test-both/util/assignWithNoUnnecessaryAllocation_test.ts b/ts/test-both/util/assignWithNoUnnecessaryAllocation_test.ts index 2a36892132ed..5a10796c1066 100644 --- a/ts/test-both/util/assignWithNoUnnecessaryAllocation_test.ts +++ b/ts/test-both/util/assignWithNoUnnecessaryAllocation_test.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { assert } from 'chai'; @@ -6,10 +6,10 @@ import { assert } from 'chai'; import { assignWithNoUnnecessaryAllocation } from '../../util/assignWithNoUnnecessaryAllocation'; describe('assignWithNoUnnecessaryAllocation', () => { - interface Person { + type Person = { name?: string; age?: number; - } + }; it('returns the same object if there are no modifications', () => { const empty = {}; diff --git a/ts/test-node/quill/mentions/matchers_test.ts b/ts/test-node/quill/mentions/matchers_test.ts index ab1d7b04b044..3c593c522490 100644 --- a/ts/test-node/quill/mentions/matchers_test.ts +++ b/ts/test-node/quill/mentions/matchers_test.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { assert } from 'chai'; @@ -69,14 +69,14 @@ const memberRepositoryRef: RefObject = { const matcher = matchMention(memberRepositoryRef); -interface Mention { +type Mention = { uuid: string; title: string; -} +}; -interface MentionInsert { +type MentionInsert = { mention: Mention; -} +}; const isMention = (insert?: unknown): insert is MentionInsert => { if (insert) { diff --git a/ts/textsecure/Crypto.ts b/ts/textsecure/Crypto.ts index 186229c1952a..42cef937f66e 100644 --- a/ts/textsecure/Crypto.ts +++ b/ts/textsecure/Crypto.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -9,6 +9,8 @@ import { ByteBufferClass } from '../window.d'; declare global { // this is fixed in already, and won't be necessary when the new definitions // files are used: https://github.com/microsoft/TSJS-lib-generator/pull/843 + // We want to extend `SubtleCrypto`, so we need an interface. + // eslint-disable-next-line no-restricted-syntax export interface SubtleCrypto { decrypt( algorithm: @@ -108,10 +110,10 @@ const PROFILE_KEY_LENGTH = 32; // bytes const PROFILE_TAG_LENGTH = 128; // bits const PROFILE_NAME_PADDED_LENGTH = 53; // bytes -interface EncryptedAttachment { +type EncryptedAttachment = { ciphertext: ArrayBuffer; digest: ArrayBuffer; -} +}; async function verifyDigest( data: ArrayBuffer, diff --git a/ts/textsecure/MessageReceiver.ts b/ts/textsecure/MessageReceiver.ts index de2653a42cf3..c1b1398edeb8 100644 --- a/ts/textsecure/MessageReceiver.ts +++ b/ts/textsecure/MessageReceiver.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable @typescript-eslint/ban-types */ @@ -49,6 +49,8 @@ const GROUPV2_ID_LENGTH = 32; const RETRY_TIMEOUT = 2 * 60 * 1000; declare global { + // We want to extend `Event`, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface Event { code?: string | number; configuration?: any; @@ -79,6 +81,8 @@ declare global { typing?: any; verified?: any; } + // We want to extend `Error`, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface Error { reason?: any; stackForLog?: string; diff --git a/ts/textsecure/SendMessage.ts b/ts/textsecure/SendMessage.ts index fd79b93796a1..4feb7bad39e2 100644 --- a/ts/textsecure/SendMessage.ts +++ b/ts/textsecure/SendMessage.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable no-nested-ternary */ @@ -70,10 +70,10 @@ export type SendOptionsType = { online?: boolean; }; -export interface CustomError extends Error { +export type CustomError = Error & { identifier?: string; number?: string; -} +}; export type CallbackResultType = { successfulIdentifiers?: Array; @@ -105,9 +105,9 @@ type GroupV1InfoType = { members: Array; }; -interface GroupCallUpdateType { +type GroupCallUpdateType = { eraId: string; -} +}; type MessageOptionsType = { attachments?: Array | null; diff --git a/ts/textsecure/Storage.ts b/ts/textsecure/Storage.ts index 548b9479433f..bd83856e3418 100644 --- a/ts/textsecure/Storage.ts +++ b/ts/textsecure/Storage.ts @@ -1,11 +1,11 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable @typescript-eslint/no-explicit-any */ import utils from './Helpers'; // Default implmentation working with localStorage -const localStorageImpl = { +const localStorageImpl: StorageInterface = { put(key: string, value: any) { if (value === undefined) { throw new Error('Tried to store undefined'); @@ -26,14 +26,14 @@ const localStorageImpl = { }, }; -export interface StorageInterface { +export type StorageInterface = { put(key: string, value: any): void | Promise; get(key: string, defaultValue: any): any; remove(key: string): void | Promise; -} +}; const Storage = { - impl: localStorageImpl as StorageInterface, + impl: localStorageImpl, put(key: string, value: unknown): Promise | void { return Storage.impl.put(key, value); diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index ca77e5e8ee75..da7a8b0d8f71 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable no-param-reassign */ @@ -586,6 +586,8 @@ async function _outerAjax(url: string | null, options: PromiseAjaxOptionsType) { } declare global { + // We want to extend `Error`, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface Error { code?: number | string; response?: any; diff --git a/ts/types/Attachment.ts b/ts/types/Attachment.ts index 5277f24379c0..6784ff6f9e17 100644 --- a/ts/types/Attachment.ts +++ b/ts/types/Attachment.ts @@ -1,4 +1,4 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import is from '@sindresorhus/is'; @@ -20,7 +20,7 @@ const MIN_HEIGHT = 50; // Used for display -export interface AttachmentType { +export type AttachmentType = { blurHash?: string; caption?: string; contentType: MIME.MIMEType; @@ -47,7 +47,7 @@ export interface AttachmentType { url: string; contentType: MIME.MIMEType; }; -} +}; // UI-focused functions @@ -288,9 +288,9 @@ export type Attachment = { // digest?: ArrayBuffer; } & Partial; -interface AttachmentSchemaVersion3 { +type AttachmentSchemaVersion3 = { path: string; -} +}; export const isVisualMedia = (attachment: Attachment): boolean => { const { contentType } = attachment; diff --git a/ts/types/Calling.ts b/ts/types/Calling.ts index a31033080822..476be86364f2 100644 --- a/ts/types/Calling.ts +++ b/ts/types/Calling.ts @@ -10,7 +10,7 @@ export enum CallMode { Group = 'Group', } -interface ActiveCallBaseType { +type ActiveCallBaseType = { conversation: ConversationType; hasLocalAudio: boolean; hasLocalVideo: boolean; @@ -20,9 +20,9 @@ interface ActiveCallBaseType { settingsDialogOpen: boolean; showParticipantsList: boolean; showSafetyNumberDialog?: boolean; -} +}; -interface ActiveDirectCallType extends ActiveCallBaseType { +type ActiveDirectCallType = ActiveCallBaseType & { callMode: CallMode.Direct; callState?: CallState; callEndedReason?: CallEndedReason; @@ -32,9 +32,9 @@ interface ActiveDirectCallType extends ActiveCallBaseType { hasRemoteVideo: boolean; } ]; -} +}; -interface ActiveGroupCallType extends ActiveCallBaseType { +type ActiveGroupCallType = ActiveCallBaseType & { callMode: CallMode.Group; connectionState: GroupCallConnectionState; conversationsWithSafetyNumberChanges: Array; @@ -43,7 +43,7 @@ interface ActiveGroupCallType extends ActiveCallBaseType { deviceCount: number; peekedParticipants: Array; remoteParticipants: Array; -} +}; export type ActiveCallType = ActiveDirectCallType | ActiveGroupCallType; @@ -96,28 +96,28 @@ export enum GroupCallJoinState { Joined = 2, } -export interface GroupCallRemoteParticipantType extends ConversationType { +export type GroupCallRemoteParticipantType = ConversationType & { demuxId: number; hasRemoteAudio: boolean; hasRemoteVideo: boolean; speakerTime?: number; videoAspectRatio: number; -} +}; // Similar to RingRTC's `VideoRequest` but without the `framerate` property. -export interface GroupCallVideoRequest { +export type GroupCallVideoRequest = { demuxId: number; width: number; height: number; -} +}; // Should match RingRTC's VideoFrameSource -export interface VideoFrameSource { +export type VideoFrameSource = { receiveVideoFrame(buffer: ArrayBuffer): [number, number] | undefined; -} +}; // Must be kept in sync with RingRTC.AudioDevice -export interface AudioDevice { +export type AudioDevice = { // Device name. name: string; // Index of this device, starting from 0. @@ -126,7 +126,7 @@ export interface AudioDevice { uniqueId: string; // If present, the identifier of a localized string to substitute for the device name. i18nKey?: string; -} +}; export enum CallingDeviceType { CAMERA, @@ -143,21 +143,21 @@ export type MediaDeviceSettings = { selectedCamera: string | undefined; }; -interface DirectCallHistoryDetailsType { +type DirectCallHistoryDetailsType = { callMode: CallMode.Direct; wasIncoming: boolean; wasVideoCall: boolean; wasDeclined: boolean; acceptedTime?: number; endedTime: number; -} +}; -interface GroupCallHistoryDetailsType { +type GroupCallHistoryDetailsType = { callMode: CallMode.Group; creatorUuid: string; eraId: string; startedTime: number; -} +}; export type CallHistoryDetailsType = | DirectCallHistoryDetailsType diff --git a/ts/types/Contact.tsx b/ts/types/Contact.tsx index 3ec8f0b768c1..c655e0219815 100644 --- a/ts/types/Contact.tsx +++ b/ts/types/Contact.tsx @@ -1,9 +1,9 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { format as formatPhoneNumber } from './PhoneNumber'; -export interface ContactType { +export type ContactType = { name?: Name; number?: Array; email?: Array; @@ -11,16 +11,16 @@ export interface ContactType { avatar?: Avatar; organization?: string; signalAccount?: string; -} +}; -interface Name { +type Name = { givenName?: string; familyName?: string; prefix?: string; suffix?: string; middleName?: string; displayName?: string; -} +}; export enum ContactFormType { HOME = 1, @@ -35,19 +35,19 @@ export enum AddressType { CUSTOM = 3, } -export interface Phone { +export type Phone = { value: string; type: ContactFormType; label?: string; -} +}; -export interface Email { +export type Email = { value: string; type: ContactFormType; label?: string; -} +}; -export interface PostalAddress { +export type PostalAddress = { type: AddressType; label?: string; street?: string; @@ -57,18 +57,18 @@ export interface PostalAddress { region?: string; postcode?: string; country?: string; -} +}; -interface Avatar { +type Avatar = { avatar: Attachment; isProfile: boolean; -} +}; -interface Attachment { +type Attachment = { path?: string; error?: boolean; pending?: boolean; -} +}; export function contactSelector( contact: ContactType, diff --git a/ts/types/Util.ts b/ts/types/Util.ts index 0f879aac7ab2..daa7319f14c2 100644 --- a/ts/types/Util.ts +++ b/ts/types/Util.ts @@ -1,13 +1,13 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -export interface BodyRangeType { +export type BodyRangeType = { start: number; length: number; mentionUuid: string; replacementText: string; conversationID?: string; -} +}; export type BodyRangesType = Array; diff --git a/ts/types/message/LinkPreviews.ts b/ts/types/message/LinkPreviews.ts index a09ed1991742..9b97dda853cf 100644 --- a/ts/types/message/LinkPreviews.ts +++ b/ts/types/message/LinkPreviews.ts @@ -3,7 +3,7 @@ import { AttachmentType } from '../Attachment'; -export interface LinkPreviewType { +export type LinkPreviewType = { title: string; description?: string; domain: string; @@ -11,4 +11,4 @@ export interface LinkPreviewType { isStickerPack: boolean; image?: AttachmentType; date?: number; -} +}; diff --git a/ts/util/GoogleChrome.ts b/ts/util/GoogleChrome.ts index ef0761ef8a1e..125604e56666 100644 --- a/ts/util/GoogleChrome.ts +++ b/ts/util/GoogleChrome.ts @@ -1,11 +1,9 @@ -// Copyright 2018-2020 Signal Messenger, LLC +// Copyright 2018-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import * as MIME from '../types/MIME'; -interface MIMETypeSupportMap { - [key: string]: boolean; -} +type MIMETypeSupportMap = Record; // See: https://en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support const SUPPORTED_IMAGE_MIME_TYPES: MIMETypeSupportMap = { diff --git a/ts/util/batcher.ts b/ts/util/batcher.ts index 99b6c5d7ba76..b219415c1693 100644 --- a/ts/util/batcher.ts +++ b/ts/util/batcher.ts @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import PQueue from 'p-queue'; @@ -6,6 +6,8 @@ import PQueue from 'p-queue'; import { sleep } from './sleep'; declare global { + // We want to extend `window`'s properties, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface Window { // eslint-disable-next-line @typescript-eslint/no-explicit-any batchers: Array>; diff --git a/ts/util/callingNotification.ts b/ts/util/callingNotification.ts index b302994bf4dd..bde2c2cfb95e 100644 --- a/ts/util/callingNotification.ts +++ b/ts/util/callingNotification.ts @@ -1,20 +1,20 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { LocalizerType } from '../types/Util'; import { CallMode } from '../types/Calling'; import { missingCaseError } from './missingCaseError'; -interface DirectCallNotificationType { +type DirectCallNotificationType = { callMode: CallMode.Direct; wasIncoming: boolean; wasVideoCall: boolean; wasDeclined: boolean; acceptedTime?: number; endedTime: number; -} +}; -interface GroupCallNotificationType { +type GroupCallNotificationType = { activeCallConversationId?: string; callMode: CallMode.Group; conversationId: string; @@ -27,7 +27,7 @@ interface GroupCallNotificationType { deviceCount: number; maxDevices: number; startedTime: number; -} +}; export type CallingNotificationType = | DirectCallNotificationType diff --git a/ts/util/getAnimatedPngDataIfExists.ts b/ts/util/getAnimatedPngDataIfExists.ts index 51c7a21e4390..be0bed05573b 100644 --- a/ts/util/getAnimatedPngDataIfExists.ts +++ b/ts/util/getAnimatedPngDataIfExists.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only const PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); @@ -6,9 +6,9 @@ const ACTL_CHUNK_BYTES = new TextEncoder().encode('acTL'); const IDAT_CHUNK_BYTES = new TextEncoder().encode('IDAT'); const MAX_BYTES_TO_READ = 1024 * 1024; -interface AnimatedPngData { +type AnimatedPngData = { numPlays: number; -} +}; /** * This is a naïve implementation. It only performs two checks: diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index 917cf0dc40ff..51ee46b7fcd4 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -15100,7 +15100,7 @@ "rule": "jQuery-wrap(", "path": "ts/textsecure/Crypto.ts", "line": " const data = window.dcodeIO.ByteBuffer.wrap(", - "lineNumber": 348, + "lineNumber": 350, "reasonCategory": "falseMatch", "updated": "2020-04-05T23:45:16.746Z" }, @@ -15108,7 +15108,7 @@ "rule": "jQuery-wrap(", "path": "ts/textsecure/Crypto.ts", "line": " given: window.dcodeIO.ByteBuffer.wrap(padded)", - "lineNumber": 377, + "lineNumber": 379, "reasonCategory": "falseMatch", "updated": "2020-04-05T23:45:16.746Z" }, @@ -15116,7 +15116,7 @@ "rule": "jQuery-wrap(", "path": "ts/textsecure/Crypto.ts", "line": " ? window.dcodeIO.ByteBuffer.wrap(padded)", - "lineNumber": 381, + "lineNumber": 383, "reasonCategory": "falseMatch", "updated": "2020-04-05T23:45:16.746Z" }, @@ -15196,7 +15196,7 @@ "rule": "jQuery-wrap(", "path": "ts/textsecure/WebAPI.ts", "line": " const byteBuffer = window.dcodeIO.ByteBuffer.wrap(", - "lineNumber": 2177, + "lineNumber": 2179, "reasonCategory": "falseMatch", "updated": "2020-09-08T23:07:22.682Z" }, diff --git a/ts/util/sniffImageMimeType.ts b/ts/util/sniffImageMimeType.ts index db2113feb279..de5357a574db 100644 --- a/ts/util/sniffImageMimeType.ts +++ b/ts/util/sniffImageMimeType.ts @@ -1,4 +1,4 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { @@ -29,11 +29,11 @@ export function sniffImageMimeType( return undefined; } -interface Type { +type Type = { mimeType: MIMEType; bytePattern: Uint8Array; patternMask?: Uint8Array; -} +}; const TYPES: Array = [ { mimeType: IMAGE_ICO, diff --git a/ts/util/sortByTitle.ts b/ts/util/sortByTitle.ts index 1e9d346d65a8..28efb1febbf2 100644 --- a/ts/util/sortByTitle.ts +++ b/ts/util/sortByTitle.ts @@ -1,9 +1,9 @@ -// Copyright 2020 Signal Messenger, LLC +// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -interface HasTitle { +type HasTitle = { title: string; -} +}; export function sortByTitle( arr: ReadonlyArray diff --git a/ts/util/waitBatcher.ts b/ts/util/waitBatcher.ts index c9d6775dda01..4a8d9607840f 100644 --- a/ts/util/waitBatcher.ts +++ b/ts/util/waitBatcher.ts @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Signal Messenger, LLC +// Copyright 2019-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import PQueue from 'p-queue'; @@ -6,6 +6,8 @@ import PQueue from 'p-queue'; import { sleep } from './sleep'; declare global { + // We want to extend `window`'s properties, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface Window { // eslint-disable-next-line @typescript-eslint/no-explicit-any waitBatchers: Array>; diff --git a/ts/views/conversation_view.ts b/ts/views/conversation_view.ts index 074a0b99e263..e6cdf668caf0 100644 --- a/ts/views/conversation_view.ts +++ b/ts/views/conversation_view.ts @@ -9,7 +9,7 @@ // see https://github.com/microsoft/TypeScript/issues/41562 type GroupV2PendingMemberType = import('../model-types.d').GroupV2PendingMemberType; -interface GetLinkPreviewResult { +type GetLinkPreviewResult = { title: string; url: string; image: { @@ -21,7 +21,7 @@ interface GetLinkPreviewResult { }; description: string | null; date: number | null; -} +}; const FIVE_MINUTES = 1000 * 60 * 5; const LINK_PREVIEW_TIMEOUT = 60 * 1000; diff --git a/ts/window.d.ts b/ts/window.d.ts index f12fa39203c7..342abde5d96b 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -87,6 +87,8 @@ type ConfirmationDialogViewProps = { }; declare global { + // We want to extend `window`'s properties, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface Window { _: typeof Underscore; $: typeof jQuery; @@ -501,6 +503,8 @@ declare global { GV2_MIGRATION_DISABLE_INVITE: boolean; } + // We want to extend `Error`, so we need an interface. + // eslint-disable-next-line no-restricted-syntax interface Error { cause?: Event; }