From 804c3706ffb2bb4a0f120b598f6ff2a3cafcb812 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Thu, 23 May 2024 15:02:38 -0700 Subject: [PATCH] tsconfig: Enable noUncheckedIndexedAccess. Signed-off-by: Anders Kaseorg --- tsconfig.json | 1 + web/shared/src/fenced_code.ts | 8 +-- web/shared/src/typeahead.ts | 4 +- web/src/about_zulip.ts | 4 +- web/src/audible_notifications.ts | 2 +- web/src/billing/helpers.ts | 2 +- web/src/bootstrap_typeahead.ts | 24 +++---- web/src/buddy_list.ts | 4 +- web/src/color_data.ts | 2 +- web/src/common.ts | 2 +- web/src/components.ts | 4 +- web/src/compose_fade.ts | 2 +- web/src/compose_recipient.ts | 2 +- web/src/compose_ui.ts | 6 +- web/src/composebox_typeahead.ts | 12 ++-- web/src/condense.ts | 2 +- web/src/copy_and_paste.ts | 4 +- web/src/drafts.ts | 2 +- web/src/filter.ts | 16 ++--- web/src/flatpickr.ts | 2 +- web/src/hash_parser.ts | 4 +- web/src/hash_util.ts | 2 +- web/src/inbox_ui.ts | 8 +-- web/src/input_pill.ts | 14 ++-- web/src/integration_url_modal.ts | 4 +- web/src/invite.ts | 8 +-- web/src/lightbox.ts | 16 ++--- web/src/linkifiers.ts | 4 +- web/src/list_widget.ts | 4 +- web/src/markdown.ts | 6 +- web/src/message_edit_history.ts | 2 +- web/src/message_list_data.ts | 4 +- web/src/message_viewport.ts | 6 +- web/src/messages_overlay_ui.ts | 20 +++--- web/src/narrow_banner.ts | 4 +- web/src/narrow_title.ts | 4 +- web/src/people.ts | 2 +- web/src/playground_links_popover.ts | 2 +- web/src/poll_modal.ts | 2 +- web/src/popover_menus.ts | 4 +- web/src/portico/hello.ts | 4 +- web/src/portico/tabbed-instructions.ts | 2 +- web/src/read_receipts.ts | 2 +- web/src/recent_view_ui.ts | 8 +-- web/src/rendered_markdown.ts | 4 +- web/src/scroll_util.ts | 6 +- web/src/settings_components.ts | 20 +++--- web/src/settings_emoji.ts | 2 +- web/src/settings_linkifiers.ts | 2 +- web/src/settings_profile_fields.ts | 6 +- web/src/settings_realm_domains.ts | 2 +- web/src/setup.ts | 2 +- web/src/spoilers.ts | 2 +- web/src/stats/stats.ts | 96 +++++++++++++------------- web/src/stream_data.ts | 2 +- web/src/tippyjs.ts | 2 +- web/src/topic_generator.ts | 14 ++-- web/src/typeahead_helper.ts | 2 +- web/src/upload_widget.ts | 4 +- web/src/util.ts | 4 +- web/src/vdom.ts | 4 +- 61 files changed, 210 insertions(+), 205 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 74ea340173..964ffa1ae7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,7 @@ "forceConsistentCasingInFileNames": true, "isolatedModules": true, "noImplicitOverride": true, + "noUncheckedIndexedAccess": true, /* Additional checks */ "noUnusedLocals": true, diff --git a/web/shared/src/fenced_code.ts b/web/shared/src/fenced_code.ts index 5b30b2745a..0c78609834 100644 --- a/web/shared/src/fenced_code.ts +++ b/web/shared/src/fenced_code.ts @@ -215,9 +215,9 @@ export function process_fenced_code(content: string): string { consume_line = function consume_line(output_lines: string[], line: string) { const match = fence_re.exec(line); if (match) { - const fence = match[1]; - const lang = match[3]; - const header = match[5]; + const fence = match[1]!; + const lang = match[3]!; + const header = match[5]!; const handler = handler_for_fence(output_lines, fence, lang, header); handler_stack.push(handler); } else { @@ -255,7 +255,7 @@ export function get_unused_fence(content: string): string { let match; fence_length_re.lastIndex = 0; while ((match = fence_length_re.exec(content)) !== null) { - length = Math.max(length, match[1].length + 1); + length = Math.max(length, match[1]!.length + 1); } return "`".repeat(length); } diff --git a/web/shared/src/typeahead.ts b/web/shared/src/typeahead.ts index 9e3e84af67..b515c3b4dd 100644 --- a/web/shared/src/typeahead.ts +++ b/web/shared/src/typeahead.ts @@ -68,12 +68,12 @@ export function last_prefix_match(prefix: string, words: string[]): number | nul let found = false; while (left < right) { const mid = Math.floor((left + right) / 2); - if (words[mid].startsWith(prefix)) { + if (words[mid]!.startsWith(prefix)) { // Note that left can never be 0 if `found` is true, // since it is incremented at least once here. left = mid + 1; found = true; - } else if (words[mid] < prefix) { + } else if (words[mid]! < prefix) { left = mid + 1; } else { right = mid; diff --git a/web/src/about_zulip.ts b/web/src/about_zulip.ts index b95fd35822..ebef9af84a 100644 --- a/web/src/about_zulip.ts +++ b/web/src/about_zulip.ts @@ -19,12 +19,12 @@ export function launch(): void { const zulip_version_clipboard = new ClipboardJS("#about-zulip .fa-copy.zulip-version"); zulip_version_clipboard.on("success", () => { - show_copied_confirmation($("#about-zulip .fa-copy.zulip-version")[0]); + show_copied_confirmation($("#about-zulip .fa-copy.zulip-version")[0]!); }); const zulip_merge_base_clipboard = new ClipboardJS("#about-zulip .fa-copy.zulip-merge-base"); zulip_merge_base_clipboard.on("success", () => { - show_copied_confirmation($("#about-zulip .fa-copy.zulip-merge-base")[0]); + show_copied_confirmation($("#about-zulip .fa-copy.zulip-merge-base")[0]!); }); } diff --git a/web/src/audible_notifications.ts b/web/src/audible_notifications.ts index 173e79891b..efa8e2898a 100644 --- a/web/src/audible_notifications.ts +++ b/web/src/audible_notifications.ts @@ -22,6 +22,6 @@ export function update_notification_sound_source( if (notification_sound !== "none") { // Load it so that it is ready to be played; without this the old sound // is played. - $container_elem[0].load(); + $container_elem[0]!.load(); } } diff --git a/web/src/billing/helpers.ts b/web/src/billing/helpers.ts index 442954fb68..7b94c07578 100644 --- a/web/src/billing/helpers.ts +++ b/web/src/billing/helpers.ts @@ -158,7 +158,7 @@ export function update_discount_details( } export function is_valid_input($elem: JQuery): boolean { - return $elem[0].checkValidity(); + return $elem[0]!.checkValidity(); } export function redirect_to_billing_with_successful_upgrade(billing_base_url: string): void { diff --git a/web/src/bootstrap_typeahead.ts b/web/src/bootstrap_typeahead.ts index f9692720ef..0551725169 100644 --- a/web/src/bootstrap_typeahead.ts +++ b/web/src/bootstrap_typeahead.ts @@ -279,7 +279,7 @@ export class Typeahead { } select(e?: JQuery.ClickEvent | JQuery.KeyUpEvent | JQuery.KeyDownEvent): this { - const val = this.values.get(this.$menu.find(".active")[0]); + const val = this.values.get(this.$menu.find(".active")[0]!); assert(val !== undefined); if (this.input_element.type === "contenteditable") { this.input_element.$element @@ -295,8 +295,8 @@ export class Typeahead { const [from, to_before, to_after] = get_string_diff(element_val, after_text); const replacement = after_text.slice(from, to_after); // select / highlight the minimal text to be replaced - this.input_element.$element[0].setSelectionRange(from, to_before); - insertTextIntoField(this.input_element.$element[0], replacement); + this.input_element.$element[0]!.setSelectionRange(from, to_before); + insertTextIntoField(this.input_element.$element[0]!, replacement); this.input_element.$element.trigger("change"); } @@ -304,7 +304,7 @@ export class Typeahead { } set_value(): void { - const val = this.values.get(this.$menu.find(".active")[0]); + const val = this.values.get(this.$menu.find(".active")[0]!); assert(typeof val === "string"); if (this.input_element.type === "contenteditable") { this.input_element.$element.text(val); @@ -340,7 +340,7 @@ export class Typeahead { // We don't need tippy to position typeaheads which already know where they should be. return this; } - this.instance = tippy.default(this.input_element.$element[0], { + this.instance = tippy.default(this.input_element.$element[0]!, { // Lets typeahead take the width needed to fit the content // and wraps it if it overflows the visible container. maxWidth: "none", @@ -371,7 +371,7 @@ export class Typeahead { interactive: true, appendTo: () => document.body, showOnCreate: true, - content: this.$container[0], + content: this.$container[0]!, // We expect the typeahead creator to handle when to hide / show the typeahead. trigger: "manual", arrow: false, @@ -449,7 +449,7 @@ export class Typeahead { render(final_items: ItemType[], matching_items: ItemType[]): this { const $items: JQuery[] = final_items.map((item) => { const $i = $(ITEM_HTML); - this.values.set($i[0], item); + this.values.set($i[0]!, item); const item_html = this.highlighter_html(item, this.query) ?? ""; const $item_html = $i.find("a").html(item_html); @@ -461,7 +461,7 @@ export class Typeahead { return $i; }); - $items[0].addClass("active"); + $items[0]!.addClass("active"); this.$menu.empty().append($items); return this; } @@ -621,12 +621,12 @@ export class Typeahead { this.select(e); - if (this.input_element.$element[0].id === "stream_message_recipient_topic") { + if (this.input_element.$element[0]!.id === "stream_message_recipient_topic") { assert(this.input_element.type === "input"); // Move the cursor to the end of the topic const topic_length = this.input_element.$element.val()!.length; - this.input_element.$element[0].selectionStart = topic_length; - this.input_element.$element[0].selectionEnd = topic_length; + this.input_element.$element[0]!.selectionStart = topic_length; + this.input_element.$element[0]!.selectionEnd = topic_length; } break; @@ -653,7 +653,7 @@ export class Typeahead { // when shift (keycode 16) + tabbing to the topic field if ( pseudo_keycode === 16 && - this.input_element.$element[0].id === "stream_message_recipient_topic" + this.input_element.$element[0]!.id === "stream_message_recipient_topic" ) { return; } diff --git a/web/src/buddy_list.ts b/web/src/buddy_list.ts index cc0476b2a3..d88d2a1b0c 100644 --- a/web/src/buddy_list.ts +++ b/web/src/buddy_list.ts @@ -170,7 +170,7 @@ export class BuddyList extends BuddyListConf { // This will default to "bottom" placement for this tooltip. placement = "auto"; } - tippy.default($elem[0], { + tippy.default($elem[0]!, { // Because the buddy list subheadings are potential click targets // for purposes having nothing to do with the subscriber count // (collapsing/expanding), we delay showing the tooltip until the @@ -765,7 +765,7 @@ export class BuddyList extends BuddyListConf { const elem = scroll_util .get_scroll_element($(this.scroll_container_selector)) - .expectOne()[0]; + .expectOne()[0]!; // Add a fudge factor. height += 10; diff --git a/web/src/color_data.ts b/web/src/color_data.ts index 04cb86d738..106257fcaf 100644 --- a/web/src/color_data.ts +++ b/web/src/color_data.ts @@ -62,7 +62,7 @@ export function claim_colors(subs: {color: string}[]): void { } export function pick_color(): string { - const color = unused_colors[0]; + const color = unused_colors[0]!; claim_color(color); diff --git a/web/src/common.ts b/web/src/common.ts index ade48f1219..818638a1ef 100644 --- a/web/src/common.ts +++ b/web/src/common.ts @@ -108,7 +108,7 @@ function set_password_toggle_label( ): void { $(password_selector).attr("aria-label", label); if (tippy_tooltips) { - const element: tippy.ReferenceElement = $(password_selector)[0]; + const element: tippy.ReferenceElement = $(password_selector)[0]!; const tippy_instance = element._tippy ?? tippy.default(element); tippy_instance.setContent(label); } else { diff --git a/web/src/components.ts b/web/src/components.ts index fbaabe2abb..6afb8a81f1 100644 --- a/web/src/components.ts +++ b/web/src/components.ts @@ -82,7 +82,7 @@ export function toggle(opts: { meta.idx = idx; if (opts.callback) { - opts.callback(opts.values[idx].label, opts.values[idx].key); + opts.callback(opts.values[idx]!.label, opts.values[idx]!.key); } if (!opts.child_wants_focus) { @@ -162,7 +162,7 @@ export function toggle(opts: { value() { if (meta.idx >= 0) { - return opts.values[meta.idx].label; + return opts.values[meta.idx]!.label; } /* istanbul ignore next */ return undefined; diff --git a/web/src/compose_fade.ts b/web/src/compose_fade.ts index cf71a8069c..03108dca4f 100644 --- a/web/src/compose_fade.ts +++ b/web/src/compose_fade.ts @@ -213,7 +213,7 @@ export function update_rendered_message_groups( // the other code takes advantage of blocks beneath recipient bars. for (const message_group of message_groups) { const $elt = get_element(message_group); - const first_message = message_group.message_containers[0].msg; + const first_message = message_group.message_containers[0]!.msg; const should_fade = compose_fade_helper.should_fade_message(first_message); change_fade_state($elt, should_fade); } diff --git a/web/src/compose_recipient.ts b/web/src/compose_recipient.ts index fb8e06ee0f..33a2594c56 100644 --- a/web/src/compose_recipient.ts +++ b/web/src/compose_recipient.ts @@ -314,7 +314,7 @@ function on_hidden_callback(): void { // Always move focus to the topic input even if it's not empty, // since it's likely the user will want to update the topic // after updating the stream. - ui_util.place_caret_at_end($("input#stream_message_recipient_topic")[0]); + ui_util.place_caret_at_end($("input#stream_message_recipient_topic")[0]!); } else { if (compose_state.private_message_recipient().length === 0) { $("#private_message_recipient").trigger("focus").trigger("select"); diff --git a/web/src/compose_ui.ts b/web/src/compose_ui.ts index e8d42a6c5b..30cfe6a46c 100644 --- a/web/src/compose_ui.ts +++ b/web/src/compose_ui.ts @@ -98,9 +98,9 @@ export function insert_and_scroll_into_view( replace_all = false, ): void { if (replace_all) { - setFieldText($textarea[0], content); + setFieldText($textarea[0]!, content); } else { - insertTextIntoField($textarea[0], content); + insertTextIntoField($textarea[0]!, content); } // Blurring and refocusing ensures the cursor / selection is in view // in chromium browsers. @@ -275,7 +275,7 @@ export function replace_syntax( // for details. const old_text = $textarea.val(); - replaceFieldText($textarea[0], old_syntax, () => new_syntax, "after-replacement"); + replaceFieldText($textarea[0]!, old_syntax, () => new_syntax, "after-replacement"); const new_text = $textarea.val(); // When replacing content in a textarea, we need to move the cursor diff --git a/web/src/composebox_typeahead.ts b/web/src/composebox_typeahead.ts index 81d3bef50a..7661b9c6b3 100644 --- a/web/src/composebox_typeahead.ts +++ b/web/src/composebox_typeahead.ts @@ -250,7 +250,7 @@ function handle_bulleting_or_numbering( if (bulleted_numbered_list_util.strip_bullet(previous_line) === "") { // below we select and replace the last 2 characters in the textarea before // the cursor - the bullet syntax - with an empty string - $textarea[0].setSelectionRange($textarea.caret() - 2, $textarea.caret()); + $textarea[0]!.setSelectionRange($textarea.caret() - 2, $textarea.caret()); compose_ui.insert_and_scroll_into_view("", $textarea); e.preventDefault(); return; @@ -264,7 +264,7 @@ function handle_bulleting_or_numbering( if (bulleted_numbered_list_util.strip_numbering(previous_line) === "") { // below we select then replaces the last few characters in the textarea before // the cursor - the numbering syntax - with an empty string - $textarea[0].setSelectionRange( + $textarea[0]!.setSelectionRange( $textarea.caret() - previous_number_string.length - 2, $textarea.caret(), ); @@ -297,7 +297,7 @@ export function handle_enter($textarea: JQuery, e: JQuery.K // If the selectionStart and selectionEnd are not the same, that // means that some text was selected. - if ($textarea[0].selectionStart !== $textarea[0].selectionEnd) { + if ($textarea[0]!.selectionStart !== $textarea[0]!.selectionEnd) { // Replace it with the newline, remembering to resize the // textarea if needed. compose_ui.insert_and_scroll_into_view("\n", $textarea); @@ -431,7 +431,7 @@ export function tokenize_compose_str(s: string): string { case "_": if (i === 0) { return s; - } else if (/[\s"'(/<[{]/.test(s[i - 1])) { + } else if (/[\s"'(/<[{]/.test(s[i - 1]!)) { return s.slice(i); } break; @@ -775,7 +775,7 @@ export function get_candidates( // We will likely want to extend this list to be more i18n-friendly. const terminal_symbols = ",.;?!()[]> \"'\n\t"; - if (rest !== "" && !terminal_symbols.includes(rest[0])) { + if (rest !== "" && !terminal_symbols.includes(rest[0]!)) { return []; } @@ -1158,7 +1158,7 @@ export function content_typeahead_selected( $textbox.caret(beginning.length); compose_ui.autosize_textarea($textbox); }; - flatpickr.show_flatpickr(input_element.$element[0], on_timestamp_selection, timestamp); + flatpickr.show_flatpickr(input_element.$element[0]!, on_timestamp_selection, timestamp); return beginning + rest; } } diff --git a/web/src/condense.ts b/web/src/condense.ts index f56d508e63..96ed545bb4 100644 --- a/web/src/condense.ts +++ b/web/src/condense.ts @@ -151,7 +151,7 @@ function get_message_height(elem: HTMLElement): number { // This needs to be very fast. This function runs hundreds of times // when displaying a message feed view that has hundreds of message // history, which ideally should render in <100ms. - return $(elem).find(".message_content")[0].scrollHeight; + return $(elem).find(".message_content")[0]!.scrollHeight; } export function hide_message_expander($row: JQuery): void { diff --git a/web/src/copy_and_paste.ts b/web/src/copy_and_paste.ts index debf9d0c21..5220dd8f3b 100644 --- a/web/src/copy_and_paste.ts +++ b/web/src/copy_and_paste.ts @@ -132,7 +132,7 @@ function select_div($div: JQuery, selection: Selection): void { background: "#FFF", }).attr("id", "copytempdiv"); $("body").append($div); - selection.selectAllChildren($div[0]); + selection.selectAllChildren($div[0]!); } function remove_div(_div: JQuery, ranges: Range[]): void { @@ -591,7 +591,7 @@ function is_safe_url_paste_target($textarea: JQuery): boole // Look at the two characters before the start of the original // range in search of the tell-tale `](` from existing Markdown // link syntax - const possible_markdown_link_markers = $textarea[0].value.slice(range.start - 2, range.start); + const possible_markdown_link_markers = $textarea[0]!.value.slice(range.start - 2, range.start); if (possible_markdown_link_markers === "](") { return false; diff --git a/web/src/drafts.ts b/web/src/drafts.ts index 21b0c1d6b9..29f59cbc20 100644 --- a/web/src/drafts.ts +++ b/web/src/drafts.ts @@ -367,7 +367,7 @@ function draft_notify(): void { content: $t({defaultMessage: "Saved as draft"}), arrow: true, placement: "right", - })[0]; + })[0]!; instance.show(); function remove_instance(): void { instance.destroy(); diff --git a/web/src/filter.ts b/web/src/filter.ts index e8faac8b47..f62c84be1f 100644 --- a/web/src/filter.ts +++ b/web/src/filter.ts @@ -86,7 +86,7 @@ function zephyr_topic_name_match(message: Message & {type: "stream"}, operand: s const m = /^(.*?)(?:\.d)*$/i.exec(operand); // m should never be null because any string matches that regex. assert(m !== null); - const base_topic = m[1]; + const base_topic = m[1]!; let related_regexp; // Additionally, Zephyr users expect the empty instance and @@ -257,7 +257,7 @@ export class Filter { constructor(terms: NarrowTerm[]) { this._terms = this.fix_terms(terms); if (this.has_operator("channel")) { - this._sub = stream_data.get_sub_by_name(this.operands("channel")[0]); + this._sub = stream_data.get_sub_by_name(this.operands("channel")[0]!); } } @@ -724,7 +724,7 @@ export class Filter { } is_non_huddle_pm(): boolean { - return this.has_operator("dm") && this.operands("dm")[0].split(",").length === 1; + return this.has_operator("dm") && this.operands("dm")[0]!.split(",").length === 1; } supports_collapsing_recipients(): boolean { @@ -872,7 +872,7 @@ export class Filter { "/#narrow/" + CHANNEL_SYNONYM + "/" + - stream_data.name_to_slug(this.operands("channel")[0]) + + stream_data.name_to_slug(this.operands("channel")[0]!) + "/topic/" + this.operands("topic")[0] ); @@ -894,7 +894,7 @@ export class Filter { "/#narrow/" + CHANNEL_SYNONYM + "/" + - stream_data.name_to_slug(this.operands("channel")[0]) + stream_data.name_to_slug(this.operands("channel")[0]!) ); case "is-dm": return "/#narrow/is/dm"; @@ -911,7 +911,7 @@ export class Filter { // TODO: It is ambiguous how we want to handle the 'sender' case, // we may remove it in the future based on design decisions case "sender": - return "/#narrow/sender/" + people.emails_to_slug(this.operands("sender")[0]); + return "/#narrow/sender/" + people.emails_to_slug(this.operands("sender")[0]!); } } @@ -991,7 +991,7 @@ export class Filter { (term_types.length === 2 && _.isEqual(term_types, ["dm", "near"])) || (term_types.length === 1 && _.isEqual(term_types, ["dm"])) ) { - const emails = this.operands("dm")[0].split(","); + const emails = this.operands("dm")[0]!.split(","); const names = emails.map((email) => { const person = people.get_by_email(email); if (!person) { @@ -1006,7 +1006,7 @@ export class Filter { return util.format_array_as_list(names, "long", "conjunction"); } if (term_types.length === 1 && _.isEqual(term_types, ["sender"])) { - const email = this.operands("sender")[0]; + const email = this.operands("sender")[0]!; const user = people.get_by_email(email); let sender = email; if (user) { diff --git a/web/src/flatpickr.ts b/web/src/flatpickr.ts index 7e5930346f..22af1b3800 100644 --- a/web/src/flatpickr.ts +++ b/web/src/flatpickr.ts @@ -25,7 +25,7 @@ export function show_flatpickr( ): flatpickr.Instance { const $flatpickr_input = $("").attr("id", "#timestamp_flatpickr"); - flatpickr_instance = flatpickr($flatpickr_input[0], { + flatpickr_instance = flatpickr($flatpickr_input[0]!, { mode: "single", enableTime: true, clickOpens: false, diff --git a/web/src/hash_parser.ts b/web/src/hash_parser.ts index d14860bfb2..b78a24349b 100644 --- a/web/src/hash_parser.ts +++ b/web/src/hash_parser.ts @@ -1,6 +1,6 @@ export function get_hash_category(hash?: string): string { // given "#channels/subscribed", returns "channels" - return hash ? hash.replace(/^#/, "").split(/\//)[0] : ""; + return hash ? hash.replace(/^#/, "").split(/\//)[0]! : ""; } export function get_hash_section(hash?: string): string { @@ -88,7 +88,7 @@ export function is_editing_stream(desired_stream_id: number): boolean { // if the string casted to a number is valid, and another component // after exists then it's a stream name/id pair. - const stream_id = Number.parseFloat(hash_components[1]); + const stream_id = Number.parseFloat(hash_components[1]!); return stream_id === desired_stream_id; } diff --git a/web/src/hash_util.ts b/web/src/hash_util.ts index 3f5777bc44..ba21755fbd 100644 --- a/web/src/hash_util.ts +++ b/web/src/hash_util.ts @@ -164,7 +164,7 @@ export function parse_narrow(hash: string[]): NarrowTerm[] | undefined { for (i = 1; i < hash.length; i += 2) { // We don't construct URLs with an odd number of components, // but the user might write one. - let operator = internal_url.decodeHashComponent(hash[i]); + let operator = internal_url.decodeHashComponent(hash[i]!); // Do not parse further if empty operator encountered. if (operator === "") { break; diff --git a/web/src/inbox_ui.ts b/web/src/inbox_ui.ts index 1844ea37b3..b19c86ae35 100644 --- a/web/src/inbox_ui.ts +++ b/web/src/inbox_ui.ts @@ -324,7 +324,7 @@ function insert_dms(keys_to_insert: string[]): void { } if (keys_to_insert.includes(key)) { - const $previous_row = get_row_from_conversation_key(sorted_keys[i - 1]); + const $previous_row = get_row_from_conversation_key(sorted_keys[i - 1]!); $previous_row.after($(render_inbox_row(dms_dict.get(key)))); } } @@ -462,7 +462,7 @@ function insert_stream( if (stream_index === 0) { $("#inbox-streams-container").prepend($(rendered_stream)); } else { - const previous_stream_key = sorted_stream_keys[stream_index - 1]; + const previous_stream_key = sorted_stream_keys[stream_index - 1]!; $(rendered_stream).insertAfter(get_stream_container(previous_stream_key)); } return !streams_dict.get(stream_key)!.is_hidden; @@ -486,7 +486,7 @@ function insert_topics(keys: string[], stream_key: string): void { } if (keys.includes(key)) { - const $previous_row = get_row_from_conversation_key(sorted_keys[i - 1]); + const $previous_row = get_row_from_conversation_key(sorted_keys[i - 1]!); $previous_row.after($(render_inbox_row(stream_topics_data.get(key)))); } } @@ -1393,7 +1393,7 @@ function move_focus_to_visible_area(): void { } const INBOX_ROW_HEIGHT = 30; - const position = $("#inbox-filters")[0].getBoundingClientRect(); + const position = $("#inbox-filters")[0]!.getBoundingClientRect(); const inbox_center_x = (position.left + position.right) / 2; // We are aiming to get the first row if it is completely visible or the second row. const inbox_row_below_filters = position.bottom + INBOX_ROW_HEIGHT; diff --git a/web/src/input_pill.ts b/web/src/input_pill.ts index 212a5b64b1..ed1db39bb6 100644 --- a/web/src/input_pill.ts +++ b/web/src/input_pill.ts @@ -225,10 +225,10 @@ export function create(opts: InputPillCreateOptions): InputPillContainer pill.$element[0] === element); if (idx !== -1) { - store.pills[idx].$element.remove(); + store.pills[idx]!.$element.remove(); const pill = store.pills.splice(idx, 1); if (store.onPillRemove !== undefined) { - store.onPillRemove(pill[0]); + store.onPillRemove(pill[0]!); } // This is needed to run the "change" event handler registered in @@ -264,7 +264,7 @@ export function create(opts: InputPillCreateOptions): InputPillContainer(opts: InputPillCreateOptions): InputPillContainer(opts: InputPillCreateOptions): InputPillContainer(opts: InputPillCreateOptions): InputPillContainer(opts: InputPillCreateOptions): InputPillContainer { - show_copied_confirmation($("#generate-integration-url-modal .dialog_submit_button")[0]); + show_copied_confirmation( + $("#generate-integration-url-modal .dialog_submit_button")[0]!, + ); }); $override_topic.on("change", function () { diff --git a/web/src/invite.ts b/web/src/invite.ts index 4aa7b427dc..350498934a 100644 --- a/web/src/invite.ts +++ b/web/src/invite.ts @@ -206,7 +206,7 @@ function submit_invitation_form(): void { $("#invite-user-modal .dialog_submit_button").text($t({defaultMessage: "Invite"})); $("#invite-user-modal .dialog_submit_button").prop("disabled", false); $("#invite-user-modal .dialog_exit_button").prop("disabled", false); - $invite_status[0].scrollIntoView(); + $invite_status[0]!.scrollIntoView(); }, }); } @@ -226,7 +226,7 @@ function generate_multiuse_invite(): void { clipboard.on("success", () => { const tippy_timeout_in_ms = 800; show_copied_confirmation( - $("#copy_generated_invite_link")[0], + $("#copy_generated_invite_link")[0]!, () => { // Do nothing on hide }, @@ -243,7 +243,7 @@ function generate_multiuse_invite(): void { ); $("#invite-user-modal .dialog_submit_button").prop("disabled", false); $("#invite-user-modal .dialog_exit_button").prop("disabled", false); - $invite_status[0].scrollIntoView(); + $invite_status[0]!.scrollIntoView(); }, }); } @@ -308,7 +308,7 @@ function set_streams_to_join_list_visibility(): void { const realm_has_default_streams = stream_data.get_default_stream_ids().length !== 0; const hide_streams_list = realm_has_default_streams && - $("input#invite_select_default_streams")[0].checked; + $("input#invite_select_default_streams")[0]!.checked; if (hide_streams_list) { $("#streams_to_add .invite-stream-controls").hide(); $("#invite-stream-checkboxes").hide(); diff --git a/web/src/lightbox.ts b/web/src/lightbox.ts index 745a659e0f..9241313390 100644 --- a/web/src/lightbox.ts +++ b/web/src/lightbox.ts @@ -114,12 +114,12 @@ export class PanZoomControl { // See https://github.com/anvaka/panzoom/issues/112 for upstream discussion. const {scale, x, y} = e.getTransform(); - const image_width = $(".zoom-element > img")[0].clientWidth * scale; - const image_height = $(".zoom-element > img")[0].clientHeight * scale; - const zoom_element_width = $(".zoom-element")[0].clientWidth * scale; - const zoom_element_height = $(".zoom-element")[0].clientHeight * scale; - const max_translate_x = $(".image-preview")[0].clientWidth; - const max_translate_y = $(".image-preview")[0].clientHeight; + const image_width = $(".zoom-element > img")[0]!.clientWidth * scale; + const image_height = $(".zoom-element > img")[0]!.clientHeight * scale; + const zoom_element_width = $(".zoom-element")[0]!.clientWidth * scale; + const zoom_element_height = $(".zoom-element")[0]!.clientHeight * scale; + const max_translate_x = $(".image-preview")[0]!.clientWidth; + const max_translate_y = $(".image-preview")[0]!.clientHeight; // When the image is dragged out of the image-preview container // (max_translate) it will be "snapped" back so that the number @@ -377,7 +377,7 @@ export function build_open_media_function( payload = asset_map.get($preview_src); } if (payload === undefined) { - payload = parse_media_data($media[0]); + payload = parse_media_data($media[0]!); } } @@ -562,7 +562,7 @@ export function initialize(): void { // Bind the pan/zoom control the newly created element. const pan_zoom_control = new PanZoomControl( - $("#lightbox_overlay .image-preview > .zoom-element")[0], + $("#lightbox_overlay .image-preview > .zoom-element")[0]!, ); const reset_lightbox_state = function (): void { diff --git a/web/src/linkifiers.ts b/web/src/linkifiers.ts index 26d3e029c6..9992d558da 100644 --- a/web/src/linkifiers.ts +++ b/web/src/linkifiers.ts @@ -29,7 +29,7 @@ function python_to_js_linkifier( let current_group = 1; const group_number_to_name: Record = {}; while (match) { - const name = match[1]; + const name = match[1]!; // Replace named group with regular matching group pattern = pattern.replace("(?P<" + name + ">", "("); // Map numbered reference to named reference for template expansion @@ -49,7 +49,7 @@ function python_to_js_linkifier( // JS regexes only support i (case insensitivity) and m (multiline) // flags, so keep those and ignore the rest if (match) { - const py_flags = match[1]; + const py_flags = match[1]!; for (const flag of py_flags) { if ("im".includes(flag)) { diff --git a/web/src/list_widget.ts b/web/src/list_widget.ts index 479f33d9e0..8a8b92be78 100644 --- a/web/src/list_widget.ts +++ b/web/src/list_widget.ts @@ -518,10 +518,10 @@ export function create( } const rendered_row = opts.modifier_html(item, meta.filter_value); if (insert_index === meta.filtered_list.length - 1) { - const $target_row = opts.html_selector!(meta.filtered_list[insert_index - 1]); + const $target_row = opts.html_selector!(meta.filtered_list[insert_index - 1]!); $target_row.after($(rendered_row)); } else { - const $target_row = opts.html_selector!(meta.filtered_list[insert_index + 1]); + const $target_row = opts.html_selector!(meta.filtered_list[insert_index + 1]!); $target_row.before($(rendered_row)); } widget.increase_rendered_offset(); diff --git a/web/src/markdown.ts b/web/src/markdown.ts index b0458063ad..fe1ddcd995 100644 --- a/web/src/markdown.ts +++ b/web/src/markdown.ts @@ -250,7 +250,7 @@ function parse_with_options( misfeature). */ full_name = match[1]; - user_id = Number.parseInt(match[2], 10); + user_id = Number.parseInt(match[2]!, 10); if (full_name === undefined) { // For @**|id** syntax @@ -420,7 +420,7 @@ export function get_topic_links(topic: string): TopicLink[] { const template_context = Object.fromEntries( match .slice(1) - .map((matched_group, i) => [group_number_to_name[i + 1], matched_group]), + .map((matched_group, i) => [group_number_to_name[i + 1]!, matched_group]), ); const link_url = url_template.expand(template_context); // We store the starting index as well, to sort the order of occurrence of the links @@ -563,7 +563,7 @@ function handleLinkifier({ assert(item !== undefined); const {url_template, group_number_to_name} = item; const template_context = Object.fromEntries( - matches.map((match, i) => [group_number_to_name[i + 1], match]), + matches.map((match, i) => [group_number_to_name[i + 1]!, match]), ); return url_template.expand(template_context); } diff --git a/web/src/message_edit_history.ts b/web/src/message_edit_history.ts index c0759802fc..57d880e7f0 100644 --- a/web/src/message_edit_history.ts +++ b/web/src/message_edit_history.ts @@ -240,7 +240,7 @@ export function fetch_and_render_message_history(message: Message): void { .each(function () { rendered_markdown.update_elements($(this)); }); - const first_element_id = content_edit_history[0].timestamp; + const first_element_id = content_edit_history[0]!.timestamp; messages_overlay_ui.set_initial_element( String(first_element_id), keyboard_handling_context, diff --git a/web/src/message_list_data.ts b/web/src/message_list_data.ts index 1b9dda766d..248fdde584 100644 --- a/web/src/message_list_data.ts +++ b/web/src/message_list_data.ts @@ -233,7 +233,7 @@ export class MessageListData { return true; } - const recipient_id = Number.parseInt(recipients[0], 10); + const recipient_id = Number.parseInt(recipients[0]!, 10); return ( !muted_users.is_user_muted(recipient_id) && !muted_users.is_user_muted(message.sender_id) @@ -487,7 +487,7 @@ export class MessageListData { let idx = this.selected_idx() + 1; while (idx < this._items.length) { - const msg_id = this._items[idx].id; + const msg_id = this._items[idx]!.id; if (!id_set.has(msg_id)) { break; } diff --git a/web/src/message_viewport.ts b/web/src/message_viewport.ts index 2847981242..9bbdd4869b 100644 --- a/web/src/message_viewport.ts +++ b/web/src/message_viewport.ts @@ -78,7 +78,7 @@ export function message_viewport_info(): MessageViewportInfo { export function at_rendered_bottom(): boolean { const bottom = scrollTop() + height(); // This also includes bottom whitespace. - const full_height = $scroll_container[0].scrollHeight; + const full_height = $scroll_container[0]!.scrollHeight; // We only know within a pixel or two if we're // exactly at the bottom, due to browser quirkiness, @@ -94,7 +94,7 @@ export function bottom_rendered_message_visible(): boolean { const $last_row = rows.last_visible(); if ($last_row[0] !== undefined) { const message_bottom = $last_row[0].getBoundingClientRect().bottom; - const bottom_of_feed = $("#compose")[0].getBoundingClientRect().top; + const bottom_of_feed = $("#compose")[0]!.getBoundingClientRect().top; return bottom_of_feed > message_bottom; } return false; @@ -214,7 +214,7 @@ const top_of_feed = new util.CachedValue({ const bottom_of_feed = new util.CachedValue({ compute_value() { - return $("#compose")[0].getBoundingClientRect().top; + return $("#compose")[0]!.getBoundingClientRect().top; }, }); diff --git a/web/src/messages_overlay_ui.ts b/web/src/messages_overlay_ui.ts index 5a926ecd41..63fe408064 100644 --- a/web/src/messages_overlay_ui.ts +++ b/web/src/messages_overlay_ui.ts @@ -74,10 +74,10 @@ export function modals_handle_events(event_key: string, context: Context): void export function set_initial_element(element_id: string, context: Context): void { if (element_id) { const $current_element = get_element_by_id(element_id, context); - const focus_element = $current_element[0].children[0]; + const focus_element = $current_element[0]!.children[0]; assert(focus_element instanceof HTMLElement); activate_element(focus_element, context); - $(`.${CSS.escape(context.items_list_selector)}`)[0].scrollTop = 0; + $(`.${CSS.escape(context.items_list_selector)}`)[0]!.scrollTop = 0; } } @@ -132,7 +132,7 @@ function initialize_focus(event_name: string, context: Context): void { } const $element = get_element_by_id(id, context); - const focus_element = $element[0].children[0]; + const focus_element = $element[0]!.children[0]; assert(focus_element instanceof HTMLElement); activate_element(focus_element, context); } @@ -152,28 +152,28 @@ function scroll_to_element($element: JQuery, context: Context): void { const $box_item = $(`.${CSS.escape(context.box_item_selector)}`); // If focused element is first, scroll to the top. - if ($box_item.first()[0].parentElement === $element[0]) { - $items_list[0].scrollTop = 0; + if ($box_item.first()[0]!.parentElement === $element[0]) { + $items_list[0]!.scrollTop = 0; } // If focused element is last, scroll to the bottom. - if ($box_item.last()[0].parentElement === $element[0]) { - $items_list[0].scrollTop = $items_list[0].scrollHeight - ($items_list.height() ?? 0); + if ($box_item.last()[0]!.parentElement === $element[0]) { + $items_list[0]!.scrollTop = $items_list[0]!.scrollHeight - ($items_list.height() ?? 0); } // If focused element is cut off from the top, scroll up halfway in modal. if ($element.position().top < 55) { // 55 is the minimum distance from the top that will require extra scrolling. - $items_list[0].scrollTop -= $items_list[0].clientHeight / 2; + $items_list[0]!.scrollTop -= $items_list[0]!.clientHeight / 2; } // If focused element is cut off from the bottom, scroll down halfway in modal. const dist_from_top = $element.position().top; const total_dist = dist_from_top + $element[0].clientHeight; - const dist_from_bottom = $items_container[0].clientHeight - total_dist; + const dist_from_bottom = $items_container[0]!.clientHeight - total_dist; if (dist_from_bottom < -4) { // -4 is the min dist from the bottom that will require extra scrolling. - $items_list[0].scrollTop += $items_list[0].clientHeight / 2; + $items_list[0]!.scrollTop += $items_list[0]!.clientHeight / 2; } } diff --git a/web/src/narrow_banner.ts b/web/src/narrow_banner.ts index fca33ba2fc..8418d3be39 100644 --- a/web/src/narrow_banner.ts +++ b/web/src/narrow_banner.ts @@ -32,7 +32,7 @@ function retrieve_search_query_data(): SearchData { const current_filter = narrow_state.filter(); assert(current_filter !== undefined); const search_query = current_filter.operands("search")[0]; - const query_words = search_query.split(" "); + const query_words = search_query!.split(" "); const search_string_result: SearchData = { query_words: [], @@ -96,7 +96,7 @@ function pick_empty_narrow_banner(): NarrowBannerData { return default_banner; } - const first_term = current_filter.terms()[0]; + const first_term = current_filter.terms()[0]!; const first_operator = first_term.operator; const first_operand = first_term.operand; const num_terms = current_filter.terms().length; diff --git a/web/src/narrow_title.ts b/web/src/narrow_title.ts index 99455d2f1e..dfd2b9014f 100644 --- a/web/src/narrow_title.ts +++ b/web/src/narrow_title.ts @@ -47,7 +47,7 @@ export function compute_narrow_title(filter?: Filter): string { } if (filter.has_operator("dm")) { - const emails = filter.operands("dm")[0]; + const emails = filter.operands("dm")[0]!; const user_ids = people.emails_strings_to_user_ids_string(emails); if (user_ids !== undefined) { @@ -60,7 +60,7 @@ export function compute_narrow_title(filter?: Filter): string { } if (filter.has_operator("sender")) { - const user = people.get_by_email(filter.operands("sender")[0]); + const user = people.get_by_email(filter.operands("sender")[0]!); if (user) { if (people.is_my_user_id(user.user_id)) { return $t({defaultMessage: "Messages sent by you"}); diff --git a/web/src/people.ts b/web/src/people.ts index 6e88f2a703..0ee0e5ac89 100644 --- a/web/src/people.ts +++ b/web/src/people.ts @@ -737,7 +737,7 @@ export function slug_to_emails(slug: string): string | undefined { */ const m = /^([\d,]+)(-.*)?/.exec(slug); if (m) { - let user_ids_string = m[1]; + let user_ids_string = m[1]!; user_ids_string = exclude_me_from_string(user_ids_string); return user_ids_string_to_emails_string(user_ids_string); } diff --git a/web/src/playground_links_popover.ts b/web/src/playground_links_popover.ts index b2b7a20aef..5ac4fc0a76 100644 --- a/web/src/playground_links_popover.ts +++ b/web/src/playground_links_popover.ts @@ -126,7 +126,7 @@ function register_click_handlers(): void { } const popover_target = $view_in_playground_button.find( ".playground-links-popover-container", - )[0]; + )[0]!; toggle_playground_links_popover(popover_target, playground_store); } }, diff --git a/web/src/poll_modal.ts b/web/src/poll_modal.ts index b84542fbaf..7c1c3f4a29 100644 --- a/web/src/poll_modal.ts +++ b/web/src/poll_modal.ts @@ -46,7 +46,7 @@ export function poll_options_setup(): void { // setTimeout is needed to here to give time for simplebar to initialise setTimeout(() => { - SortableJS.create($("#add-poll-form .poll-options-list .simplebar-content")[0], { + SortableJS.create($("#add-poll-form .poll-options-list .simplebar-content")[0]!, { onUpdate() { // Do nothing on drag; the order is only processed on submission. }, diff --git a/web/src/popover_menus.ts b/web/src/popover_menus.ts index 8f1846ac97..b31bca2d02 100644 --- a/web/src/popover_menus.ts +++ b/web/src/popover_menus.ts @@ -192,7 +192,7 @@ export const default_popover_props: Partial = { // $tippy_box[0].hasAttribute("data-reference-hidden"); is the real check // but linter wants us to write it like this. const is_reference_outside_window = Object.hasOwn( - $tippy_box[0].dataset, + $tippy_box[0]!.dataset, "referenceHidden", ); if (is_reference_outside_window) { @@ -215,7 +215,7 @@ export const default_popover_props: Partial = { return; } - const reference_rect = $reference[0].getBoundingClientRect(); + const reference_rect = $reference[0]!.getBoundingClientRect(); // This is the logic we want but since it is too expensive to run // on every scroll, we run a cheaper version of this to just check if // compose, sticky header or navbar are not obscuring the reference diff --git a/web/src/portico/hello.ts b/web/src/portico/hello.ts index e5a2a57273..994a1cb90f 100644 --- a/web/src/portico/hello.ts +++ b/web/src/portico/hello.ts @@ -8,7 +8,7 @@ function get_new_rand(old_random_int: number, max: number): number { function get_random_item_from_array(array: T[]): T { assert(array.length >= 1); - return array[Math.floor(Math.random() * array.length)]; + return array[Math.floor(Math.random() * array.length)]!; } const current_client_logo_class_names = new Set([ @@ -40,7 +40,7 @@ function update_client_logo(): void { current_client_logo_class_names_index, client_logos.length, ); - const client_logo_elt = client_logos[current_client_logo_class_names_index]; + const client_logo_elt = client_logos[current_client_logo_class_names_index]!; const current_logo_class = client_logo_elt.className; current_client_logo_class_names.delete(current_logo_class); diff --git a/web/src/portico/tabbed-instructions.ts b/web/src/portico/tabbed-instructions.ts index dc0a778faa..81ea0d6990 100644 --- a/web/src/portico/tabbed-instructions.ts +++ b/web/src/portico/tabbed-instructions.ts @@ -58,7 +58,7 @@ export function activate_correct_tab($tabbed_section: JQuery): void { const $active_list_items = $li.filter(".active"); if (!$active_list_items.length) { $li.first().addClass("active"); - const tab_key = $li.first()[0].dataset.tabKey; + const tab_key = $li.first()[0]!.dataset.tabKey; if (tab_key) { $blocks.filter("[data-tab-key=" + tab_key + "]").addClass("active"); } else { diff --git a/web/src/read_receipts.ts b/web/src/read_receipts.ts index 80a8d37ca4..b4cda49cf9 100644 --- a/web/src/read_receipts.ts +++ b/web/src/read_receipts.ts @@ -89,7 +89,7 @@ export function show_user_list(message_id: number): void { $("#read_receipts_modal .read_receipts_list").html( render_read_receipts(context), ); - new SimpleBar($("#read_receipts_modal .modal__content")[0]); + new SimpleBar($("#read_receipts_modal .modal__content")[0]!); } }, error(xhr) { diff --git a/web/src/recent_view_ui.ts b/web/src/recent_view_ui.ts index a0b669dcc3..d05f6d00b2 100644 --- a/web/src/recent_view_ui.ts +++ b/web/src/recent_view_ui.ts @@ -340,7 +340,7 @@ function set_table_focus(row: number, col: number, using_keyboard = false): bool if (using_keyboard) { const scroll_element = $( "#recent_view_table .table_fix_head .simplebar-content-wrapper", - )[0]; + )[0]!; const half_height_of_visible_area = scroll_element.offsetHeight / 2; const topic_offset = topic_offset_to_visible_area($topic_row); @@ -1093,14 +1093,14 @@ function topic_offset_to_visible_area($topic_row: JQuery): string | undefined { } const $scroll_container = $("#recent_view_table .table_fix_head"); const thead_height = $scroll_container.find("thead").outerHeight(true)!; - const scroll_container_props = $scroll_container[0].getBoundingClientRect(); + const scroll_container_props = $scroll_container[0]!.getBoundingClientRect(); // Since user cannot see row under thead, exclude it as part of the scroll container. const scroll_container_top = scroll_container_props.top + thead_height; const compose_height = $("#compose").outerHeight(true)!; const scroll_container_bottom = scroll_container_props.bottom - compose_height; - const topic_props = $topic_row[0].getBoundingClientRect(); + const topic_props = $topic_row[0]!.getBoundingClientRect(); // Topic is above the visible scroll region. if (topic_props.top < scroll_container_top) { @@ -1119,7 +1119,7 @@ function recenter_focus_if_off_screen(): void { return; } - const table_wrapper_element = $("#recent_view_table .table_fix_head")[0]; + const table_wrapper_element = $("#recent_view_table .table_fix_head")[0]!; const $topic_rows = $("#recent_view_table table tbody tr"); if (row_focus >= $topic_rows.length) { diff --git a/web/src/rendered_markdown.ts b/web/src/rendered_markdown.ts index 45658b3579..e45d73ab8c 100644 --- a/web/src/rendered_markdown.ts +++ b/web/src/rendered_markdown.ts @@ -312,7 +312,7 @@ export const update_elements = ($content: JQuery): void => { $view_in_playground_button.attr("aria-label", title); } const $copy_button = $buttonContainer.find(".copy_codeblock"); - const clipboard = new ClipboardJS($copy_button[0], { + const clipboard = new ClipboardJS($copy_button[0]!, { text(copy_element) { const $code = $(copy_element).parent().siblings("code"); return $code.text(); @@ -320,7 +320,7 @@ export const update_elements = ($content: JQuery): void => { }); clipboard.on("success", () => { - show_copied_confirmation($copy_button[0]); + show_copied_confirmation($copy_button[0]!); }); $codehilite.addClass("zulip-code-block"); }); diff --git a/web/src/scroll_util.ts b/web/src/scroll_util.ts index b54b591fa8..161b9452ac 100644 --- a/web/src/scroll_util.ts +++ b/web/src/scroll_util.ts @@ -5,7 +5,7 @@ import SimpleBar from "simplebar"; type JQueryOrZJQuery = {__zjquery?: true} & JQuery; export function get_content_element($element: JQuery): JQuery { - const element = $element.expectOne()[0]; + const element = $element.expectOne()[0]!; const sb = SimpleBar.instances.get(element); if (sb) { return $(sb.getContentElement()!); @@ -19,7 +19,7 @@ export function get_scroll_element($element: JQueryOrZJQuery): JQuery { return $element; } - const element = $element.expectOne()[0]; + const element = $element.expectOne()[0]!; const sb = SimpleBar.instances.get(element); if (sb) { return $(sb.getScrollElement()!); @@ -32,7 +32,7 @@ export function get_scroll_element($element: JQueryOrZJQuery): JQuery { } export function reset_scrollbar($element: JQuery): void { - const element = $element.expectOne()[0]; + const element = $element.expectOne()[0]!; const sb = SimpleBar.instances.get(element); if (sb) { sb.getScrollElement()!.scrollTop = 0; diff --git a/web/src/settings_components.ts b/web/src/settings_components.ts index aa3fcac620..1c3c6fa532 100644 --- a/web/src/settings_components.ts +++ b/web/src/settings_components.ts @@ -187,7 +187,7 @@ export function extract_property_name($elem: JQuery, for_realm_default_settings? // "realm_{settings_name}}" because both user and realm default // settings use the same template and each element should have // unique id. - return /^realm_(.*)$/.exec(elem_id.replaceAll("-", "_"))![1]; + return /^realm_(.*)$/.exec(elem_id.replaceAll("-", "_"))![1]!; } if (elem_id.startsWith("id_authmethod")) { @@ -197,14 +197,14 @@ export function extract_property_name($elem: JQuery, for_realm_default_settings? // The [\da-z]+ part of the regexp covers the auth method name itself. // We assume it's not an empty string and can contain only digits and lowercase ASCII letters, // this is ensured by a respective allowlist-based filter in populate_auth_methods(). - return /^id_authmethod[\da-z]+_(.*)$/.exec(elem_id)![1]; + return /^id_authmethod[\da-z]+_(.*)$/.exec(elem_id)![1]!; } if (elem_id.startsWith("id-custom-profile-field")) { - return /^id_custom_profile_field_(.*)$/.exec(elem_id.replaceAll("-", "_"))![1]; + return /^id_custom_profile_field_(.*)$/.exec(elem_id.replaceAll("-", "_"))![1]!; } - return /^id_(.*)$/.exec(elem_id.replaceAll("-", "_"))![1]; + return /^id_(.*)$/.exec(elem_id.replaceAll("-", "_"))![1]!; } export function get_subsection_property_elements($subsection: JQuery): HTMLElement[] { @@ -397,7 +397,7 @@ function read_select_field_data_from_form( } } $profile_field_form.find("div.choice-row").each(function (this: HTMLElement) { - const text = $(this).find("input")[0].value; + const text = $(this).find("input")[0]!.value; if (text) { let value = old_option_value_map.get(text); if (value !== undefined) { @@ -753,7 +753,7 @@ export function get_auth_method_list_data(): Record { for (const method_row of $auth_method_rows) { const method = $(method_row).attr("data-method"); assert(method !== undefined); - new_auth_methods[method] = $(method_row).find("input")[0].checked; + new_auth_methods[method] = $(method_row).find("input")[0]!.checked; } return new_auth_methods; @@ -957,11 +957,11 @@ export function populate_data_for_request( $input_elem.attr("id")!, ); assert(match_array !== null); - property_name = match_array[1]; + property_name = match_array[1]!; } else { const match_array = /^id_realm_(.*)$/.exec($input_elem.attr("id")!); assert(match_array !== null); - property_name = match_array[1]; + property_name = match_array[1]!; } if (property_name === "stream_privacy") { @@ -1146,7 +1146,7 @@ function enable_or_disable_save_button($subsection_elem: JQuery): void { const $button_wrapper = $subsection_elem.find( ".subsection-changes-save", ); - const tippy_instance = $button_wrapper[0]._tippy; + const tippy_instance = $button_wrapper[0]!._tippy; if (disable_save_btn) { // avoid duplication of tippy if (!tippy_instance) { @@ -1184,5 +1184,5 @@ export function initialize_disable_btn_hint_popover( if (hint_text !== undefined) { tippy_opts.content = hint_text; } - tippy.default($btn_wrapper[0], tippy_opts); + tippy.default($btn_wrapper[0]!, tippy_opts); } diff --git a/web/src/settings_emoji.ts b/web/src/settings_emoji.ts index 1a7d4d676f..03e68c8220 100644 --- a/web/src/settings_emoji.ts +++ b/web/src/settings_emoji.ts @@ -265,7 +265,7 @@ function show_modal(): void { } const formData = new FormData(); - const files = $("input#emoji_file_input")[0].files; + const files = $("input#emoji_file_input")[0]!.files; assert(files !== null); for (const [i, file] of [...files].entries()) { formData.append("file-" + i, file); diff --git a/web/src/settings_linkifiers.ts b/web/src/settings_linkifiers.ts index 9e72b8f7cf..501b1e5e5f 100644 --- a/web/src/settings_linkifiers.ts +++ b/web/src/settings_linkifiers.ts @@ -194,7 +194,7 @@ export function populate_linkifiers(linkifiers_data: RealmLinkifiers): void { }); if (current_user.is_admin) { - new SortableJS($linkifiers_table[0], { + new SortableJS($linkifiers_table[0]!, { onUpdate: update_linkifiers_order, handle: ".move-handle", filter: "input", diff --git a/web/src/settings_profile_fields.ts b/web/src/settings_profile_fields.ts index a681394b1d..d80eaaef98 100644 --- a/web/src/settings_profile_fields.ts +++ b/web/src/settings_profile_fields.ts @@ -421,7 +421,7 @@ function set_up_select_field_edit_form( // Add blank choice at last create_choice_row($choice_list); - SortableJS.create($choice_list[0], { + SortableJS.create($choice_list[0]!, { onUpdate() { // Do nothing on drag. We process the order on submission }, @@ -706,7 +706,7 @@ export function do_populate_profile_fields(profile_fields_data: CustomProfileFie display_in_profile_summary_fields_limit_reached = display_in_profile_summary_fields_count >= 2; if (current_user.is_admin) { - const field_list = $("#admin_profile_fields_table")[0]; + const field_list = $("#admin_profile_fields_table")[0]!; SortableJS.create(field_list, { onUpdate: update_field_order, filter: "input", @@ -724,7 +724,7 @@ function set_up_select_field(): void { create_choice_row($("#profile_field_choices")); if (current_user.is_admin) { - const choice_list = $("#profile_field_choices")[0]; + const choice_list = $("#profile_field_choices")[0]!; SortableJS.create(choice_list, { onUpdate() { // Do nothing on drag. We process the order on submission diff --git a/web/src/settings_realm_domains.ts b/web/src/settings_realm_domains.ts index 2604cdf024..adb8e3b989 100644 --- a/web/src/settings_realm_domains.ts +++ b/web/src/settings_realm_domains.ts @@ -114,7 +114,7 @@ export function setup_realm_domains_modal_handlers(): void { const domain = $widget.find(".new-realm-domain").val(); const allow_subdomains = $widget.find( "input.new-realm-domain-allow-subdomains", - )[0].checked; + )[0]!.checked; const data = { domain, allow_subdomains: JSON.stringify(allow_subdomains), diff --git a/web/src/setup.ts b/web/src/setup.ts index 8cede4f669..24ccf0d83a 100644 --- a/web/src/setup.ts +++ b/web/src/setup.ts @@ -25,7 +25,7 @@ $(() => { } $.fn.get_offset_to_window = function () { - return this[0].getBoundingClientRect(); + return this[0]!.getBoundingClientRect(); }; $.fn.expectOne = function () { diff --git a/web/src/spoilers.ts b/web/src/spoilers.ts index 94cb21c625..743f52ab54 100644 --- a/web/src/spoilers.ts +++ b/web/src/spoilers.ts @@ -21,7 +21,7 @@ function expand_spoiler($spoiler: JQuery): void { // of the content). CSS animations do not work with properties set to // `auto`, so we get the actual height of the content here and temporarily // put it explicitly on the element styling to allow the transition to work. - const spoiler_height = $spoiler[0].scrollHeight; + const spoiler_height = $spoiler[0]!.scrollHeight; $spoiler.height(`${spoiler_height}px`); // The `spoiler-content-open` class has CSS animations defined on it which // will trigger on the frame after this class change. diff --git a/web/src/stats/stats.ts b/web/src/stats/stats.ts index b8245825fe..9c7a5bf279 100644 --- a/web/src/stats/stats.ts +++ b/web/src/stats/stats.ts @@ -419,7 +419,7 @@ function populate_messages_sent_over_time(raw_data: unknown): void { .on("plotly_hover", (data) => { $("#hoverinfo").show(); document.querySelector("#hover_date")!.textContent = - data.points[0].data.text[data.points[0].pointNumber]; + data.points[0]!.data.text[data.points[0]!.pointNumber]!; const values: Plotly.Datum[] = [null, null, null]; for (const trace of data.points) { values[trace.curveNumber] = trace.y; @@ -432,16 +432,16 @@ function populate_messages_sent_over_time(raw_data: unknown): void { ]; for (const [i, value] of values.entries()) { if (value !== null) { - document.querySelector(hover_text_ids[i])!.style.display = + document.querySelector(hover_text_ids[i]!)!.style.display = "inline"; - document.querySelector(hover_value_ids[i])!.style.display = + document.querySelector(hover_value_ids[i]!)!.style.display = "inline"; - document.querySelector(hover_value_ids[i])!.textContent = + document.querySelector(hover_value_ids[i]!)!.textContent = value.toString(); } else { - document.querySelector(hover_text_ids[i])!.style.display = + document.querySelector(hover_text_ids[i]!)!.style.display = "none"; - document.querySelector(hover_value_ids[i])!.style.display = + document.querySelector(hover_value_ids[i]!)!.style.display = "none"; } } @@ -461,13 +461,13 @@ function populate_messages_sent_over_time(raw_data: unknown): void { let start; let is_boundary; if (aggregation === "day") { - start = floor_to_local_day(start_dates[0]); + start = floor_to_local_day(start_dates[0]!); is_boundary = function (date: Date) { return date.getHours() === 0; }; } else { assert(aggregation === "week"); - start = floor_to_local_week(start_dates[0]); + start = floor_to_local_week(start_dates[0]!); is_boundary = function (date: Date) { return date.getHours() === 0 && date.getDay() === 0; }; @@ -476,25 +476,25 @@ function populate_messages_sent_over_time(raw_data: unknown): void { const values: DataByUserType = {human: [], bot: [], me: []}; let current: DataByUserType = {human: 0, bot: 0, me: 0}; let i_init = 0; - if (is_boundary(start_dates[0])) { + if (is_boundary(start_dates[0]!)) { current = { - human: data.everyone.human[0], - bot: data.everyone.bot[0], - me: data.user.human[0], + human: data.everyone.human[0]!, + bot: data.everyone.bot[0]!, + me: data.user.human[0]!, }; i_init = 1; } for (let i = i_init; i < start_dates.length; i += 1) { - if (is_boundary(start_dates[i])) { - dates.push(start_dates[i]); + if (is_boundary(start_dates[i]!)) { + dates.push(start_dates[i]!); values.human.push(current.human); values.bot.push(current.bot); values.me.push(current.me); current = {human: 0, bot: 0, me: 0}; } - current.human += data.everyone.human[i]; - current.bot += data.everyone.bot[i]; - current.me += data.user.human[i]; + current.human += data.everyone.human[i]!; + current.bot += data.everyone.bot[i]!; + current.me += data.user.human[i]!; } values.human.push(current.human); values.bot.push(current.bot); @@ -563,9 +563,9 @@ function populate_messages_sent_over_time(raw_data: unknown): void { const plotDiv = document.querySelector( "#id_messages_sent_over_time", )!; - assert("visible" in plotDiv.data[0]); - assert("visible" in plotDiv.data[1]); - assert("visible" in plotDiv.data[2]); + assert("visible" in plotDiv.data[0]!); + assert("visible" in plotDiv.data[1]!); + assert("visible" in plotDiv.data[2]!); traces.me.visible = plotDiv.data[0].visible; traces.human.visible = plotDiv.data[1].visible; traces.bot.visible = plotDiv.data[2].visible; @@ -730,8 +730,8 @@ function populate_messages_sent_by_client(raw_data: unknown): void { const label_values: {label: string; value: number}[] = []; for (let i = 0; i < everyone_month.values.length; i += 1) { label_values.push({ - label: everyone_month.labels[i], - value: everyone_month.labels[i] === "Other" ? -1 : everyone_month.values[i], + label: everyone_month.labels[i]!, + value: everyone_month.labels[i] === "Other" ? -1 : everyone_month.values[i]!, }); } label_values.sort((a, b) => b.value - a.value); @@ -754,9 +754,9 @@ function populate_messages_sent_by_client(raw_data: unknown): void { text: [], }; for (let i = 0; i < plot_data.values.length; i += 1) { - if (plot_data.values[i] > 0) { - annotations.values.push(plot_data.values[i]); - annotations.labels.push(plot_data.labels[i]); + if (plot_data.values[i]! > 0) { + annotations.values.push(plot_data.values[i]!); + annotations.labels.push(plot_data.labels[i]!); annotations.text.push( " " + plot_data.labels[i] + " (" + plot_data.percentages[i] + ")", ); @@ -1044,7 +1044,7 @@ function populate_number_of_users(raw_data: unknown): void { .on("plotly_hover", (data) => { $("#users_hover_info").show(); document.querySelector("#users_hover_date")!.textContent = - data.points[0].data.text[data.points[0].pointNumber]; + data.points[0]!.data.text[data.points[0]!.pointNumber]!; const values: Plotly.Datum[] = [null, null, null]; for (const trace of data.points) { values[trace.curveNumber] = trace.y; @@ -1056,11 +1056,11 @@ function populate_number_of_users(raw_data: unknown): void { ]; for (const [i, value] of values.entries()) { if (value !== null) { - document.querySelector(hover_value_ids[i])!.style.display = + document.querySelector(hover_value_ids[i]!)!.style.display = "inline"; - document.querySelector(hover_value_ids[i])!.textContent = value.toString(); + document.querySelector(hover_value_ids[i]!)!.textContent = value.toString(); } else { - document.querySelector(hover_value_ids[i])!.style.display = + document.querySelector(hover_value_ids[i]!)!.style.display = "none"; } } @@ -1185,7 +1185,7 @@ function populate_messages_read_over_time(raw_data: unknown): void { .on("plotly_hover", (data) => { $("#read_hover_info").show(); document.querySelector("#read_hover_date")!.textContent = - data.points[0].data.text[data.points[0].pointNumber]; + data.points[0]!.data.text[data.points[0]!.pointNumber]!; const values: Plotly.Datum[] = [null, null]; for (const trace of data.points) { values[trace.curveNumber] = trace.y; @@ -1194,18 +1194,20 @@ function populate_messages_read_over_time(raw_data: unknown): void { const read_hover_value_ids = ["#read_hover_me_value", "#read_hover_everyone_value"]; for (const [i, value] of values.entries()) { if (value !== null) { - document.querySelector(read_hover_text_ids[i])!.style.display = - "inline"; document.querySelector( - read_hover_value_ids[i], + read_hover_text_ids[i]!, )!.style.display = "inline"; - document.querySelector(read_hover_value_ids[i])!.textContent = + document.querySelector( + read_hover_value_ids[i]!, + )!.style.display = "inline"; + document.querySelector(read_hover_value_ids[i]!)!.textContent = value.toString(); } else { - document.querySelector(read_hover_text_ids[i])!.style.display = - "none"; document.querySelector( - read_hover_value_ids[i], + read_hover_text_ids[i]!, + )!.style.display = "none"; + document.querySelector( + read_hover_value_ids[i]!, )!.style.display = "none"; } } @@ -1225,13 +1227,13 @@ function populate_messages_read_over_time(raw_data: unknown): void { let start; let is_boundary; if (aggregation === "day") { - start = floor_to_local_day(start_dates[0]); + start = floor_to_local_day(start_dates[0]!); is_boundary = function (date: Date) { return date.getHours() === 0; }; } else { assert(aggregation === "week"); - start = floor_to_local_week(start_dates[0]); + start = floor_to_local_week(start_dates[0]!); is_boundary = function (date: Date) { return date.getHours() === 0 && date.getDay() === 0; }; @@ -1240,19 +1242,19 @@ function populate_messages_read_over_time(raw_data: unknown): void { const values: DataByEveryoneMe = {everyone: [], me: []}; let current: DataByEveryoneMe = {everyone: 0, me: 0}; let i_init = 0; - if (is_boundary(start_dates[0])) { - current = {everyone: data.everyone.read[0], me: data.user.read[0]}; + if (is_boundary(start_dates[0]!)) { + current = {everyone: data.everyone.read[0]!, me: data.user.read[0]!}; i_init = 1; } for (let i = i_init; i < start_dates.length; i += 1) { - if (is_boundary(start_dates[i])) { - dates.push(start_dates[i]); + if (is_boundary(start_dates[i]!)) { + dates.push(start_dates[i]!); values.everyone.push(current.everyone); values.me.push(current.me); current = {everyone: 0, me: 0}; } - current.everyone += data.everyone.read[i]; - current.me += data.user.read[i]; + current.everyone += data.everyone.read[i]!; + current.me += data.user.read[i]!; } values.everyone.push(current.everyone); values.me.push(current.me); @@ -1315,8 +1317,8 @@ function populate_messages_read_over_time(raw_data: unknown): void { const plotDiv = document.querySelector( "#id_messages_read_over_time", )!; - assert("visible" in plotDiv.data[0]); - assert("visible" in plotDiv.data[1]); + assert("visible" in plotDiv.data[0]!); + assert("visible" in plotDiv.data[1]!); traces.me.visible = plotDiv.data[0].visible; traces.everyone.visible = plotDiv.data[1].visible; } diff --git a/web/src/stream_data.ts b/web/src/stream_data.ts index b00c2859b9..38092c532a 100644 --- a/web/src/stream_data.ts +++ b/web/src/stream_data.ts @@ -270,7 +270,7 @@ export function slug_to_name(slug: string): string { */ const m = /^(\d+)(-.*)?$/.exec(slug); if (m) { - const stream_id = Number.parseInt(m[1], 10); + const stream_id = Number.parseInt(m[1]!, 10); const sub = sub_store.get(stream_id); if (sub) { return sub.name; diff --git a/web/src/tippyjs.ts b/web/src/tippyjs.ts index 26b3eaa1c1..237750f9a9 100644 --- a/web/src/tippyjs.ts +++ b/web/src/tippyjs.ts @@ -285,7 +285,7 @@ export function initialize(): void { const second_line = $t({defaultMessage: "File name: {filename}"}, {filename}); $markup.append($("
"), $("").text(second_line)); } - instance.setContent($markup[0]); + instance.setContent($markup[0]!); return undefined; }, onHidden(instance) { diff --git a/web/src/topic_generator.ts b/web/src/topic_generator.ts index a830c1afea..e71f065423 100644 --- a/web/src/topic_generator.ts +++ b/web/src/topic_generator.ts @@ -19,19 +19,19 @@ export function next_topic( const curr_stream_index = streams.indexOf(curr_stream); // -1 if not found if (curr_stream_index >= 0) { - const stream = streams[curr_stream_index]; + const stream = streams[curr_stream_index]!; const topics = get_topics(stream); const curr_topic_index = topics.indexOf(curr_topic); // -1 if not found for (let i = curr_topic_index + 1; i < topics.length; i += 1) { - const topic = topics[i]; + const topic = topics[i]!; if (has_unread_messages(stream, topic)) { return {stream, topic}; } } for (let i = 0; i < curr_topic_index; i += 1) { - const topic = topics[i]; + const topic = topics[i]!; if (has_unread_messages(stream, topic)) { return {stream, topic}; } @@ -39,7 +39,7 @@ export function next_topic( } for (let i = curr_stream_index + 1; i < streams.length; i += 1) { - const stream = streams[i]; + const stream = streams[i]!; for (const topic of get_topics(stream)) { if (has_unread_messages(stream, topic)) { return {stream, topic}; @@ -48,7 +48,7 @@ export function next_topic( } for (let i = 0; i < curr_stream_index; i += 1) { - const stream = streams[i]; + const stream = streams[i]!; for (const topic of get_topics(stream)) { if (has_unread_messages(stream, topic)) { return {stream, topic}; @@ -150,13 +150,13 @@ export function get_next_unread_pm_string(curr_pm: string): string | undefined { const curr_pm_index = my_pm_strings.indexOf(curr_pm); // -1 if not found for (let i = curr_pm_index + 1; i < my_pm_strings.length; i += 1) { - if (unread.num_unread_for_user_ids_string(my_pm_strings[i]) > 0) { + if (unread.num_unread_for_user_ids_string(my_pm_strings[i]!) > 0) { return my_pm_strings[i]; } } for (let i = 0; i < curr_pm_index; i += 1) { - if (unread.num_unread_for_user_ids_string(my_pm_strings[i]) > 0) { + if (unread.num_unread_for_user_ids_string(my_pm_strings[i]!) > 0) { return my_pm_strings[i]; } } diff --git a/web/src/typeahead_helper.ts b/web/src/typeahead_helper.ts index c56e9f28d1..80db391e32 100644 --- a/web/src/typeahead_helper.ts +++ b/web/src/typeahead_helper.ts @@ -59,7 +59,7 @@ export function highlight_with_escaping_and_regex(regex: RegExp, item: string): let result = ""; for (const [i, piece] of pieces.entries()) { - if (regex.test(piece) && (i === 0 || pieces[i - 1].endsWith(" "))) { + if (regex.test(piece) && (i === 0 || pieces[i - 1]!.endsWith(" "))) { // only highlight if the matching part is a word prefix, ie // if it is the 1st piece or if there was a space before it result += "" + Handlebars.Utils.escapeExpression(piece) + ""; diff --git a/web/src/upload_widget.ts b/web/src/upload_widget.ts index c795c45731..0cc80d0c41 100644 --- a/web/src/upload_widget.ts +++ b/web/src/upload_widget.ts @@ -72,7 +72,7 @@ export function build_widget( if (files === null || files === undefined || files.length === 0) { return false; } - get_file_input()[0].files = files; + get_file_input()[0]!.files = files; e.preventDefault(); return false; }); @@ -160,7 +160,7 @@ export function build_direct_upload_widget( if (files === null || files === undefined || files.length === 0) { return false; } - get_file_input()[0].files = files; + get_file_input()[0]!.files = files; e.preventDefault(); return false; }); diff --git a/web/src/util.ts b/web/src/util.ts index 5eb2b1d6e9..68ca0d288e 100644 --- a/web/src/util.ts +++ b/web/src/util.ts @@ -35,7 +35,7 @@ export function lower_bound( while (len > 0) { step = Math.floor(len / 2); middle = first + step; - if (less(array[middle], value, middle)) { + if (less(array[middle]!, value, middle)) { first = middle; first += 1; len = len - step - 1; @@ -204,7 +204,7 @@ export function find_stream_wildcard_mentions(message_content: string): string | if (mention === null) { return null; } - return mention[2]; + return mention[2]!; } export const move_array_elements_to_front = function util_move_array_elements_to_front( diff --git a/web/src/vdom.ts b/web/src/vdom.ts index 1d0e0daf1b..898b0b5258 100644 --- a/web/src/vdom.ts +++ b/web/src/vdom.ts @@ -38,7 +38,7 @@ export function eq_array( return false; } - return a.every((item, i) => eq(item, b[i])); + return a.every((item, i) => eq(item, b[i]!)); } export function ul(opts: Options): Tag { @@ -198,7 +198,7 @@ export function update( const $child_elems = find().children(); for (const [i, new_node] of new_opts.keyed_nodes.entries()) { - const old_node = old_opts.keyed_nodes[i]; + const old_node = old_opts.keyed_nodes[i]!; if (new_node.eq(old_node)) { continue; }