tsconfig: Enable noUncheckedIndexedAccess.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2024-05-23 15:02:38 -07:00
parent 4e91572c96
commit 804c3706ff
61 changed files with 210 additions and 205 deletions

View File

@ -26,6 +26,7 @@
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"noImplicitOverride": true,
"noUncheckedIndexedAccess": true,
/* Additional checks */
"noUnusedLocals": true,

View File

@ -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);
}

View File

@ -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;

View File

@ -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]!);
});
}

View File

@ -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();
}
}

View File

@ -158,7 +158,7 @@ export function update_discount_details(
}
export function is_valid_input($elem: JQuery<HTMLFormElement>): boolean {
return $elem[0].checkValidity();
return $elem[0]!.checkValidity();
}
export function redirect_to_billing_with_successful_upgrade(billing_base_url: string): void {

View File

@ -279,7 +279,7 @@ export class Typeahead<ItemType extends string | object> {
}
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<ItemType extends string | object> {
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<ItemType extends string | object> {
}
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<ItemType extends string | object> {
// 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<ItemType extends string | object> {
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<ItemType extends string | object> {
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<ItemType extends string | object> {
return $i;
});
$items[0].addClass("active");
$items[0]!.addClass("active");
this.$menu.empty().append($items);
return this;
}
@ -621,12 +621,12 @@ export class Typeahead<ItemType extends string | object> {
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<ItemType extends string | object> {
// 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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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 {

View File

@ -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;

View File

@ -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);
}

View File

@ -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");

View File

@ -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

View File

@ -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<HTMLTextAreaElement>, 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;
}
}

View File

@ -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 {

View File

@ -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<HTMLTextAreaElement>): 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;

View File

@ -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();

View File

@ -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) {

View File

@ -25,7 +25,7 @@ export function show_flatpickr(
): flatpickr.Instance {
const $flatpickr_input = $<HTMLInputElement>("<input>").attr("id", "#timestamp_flatpickr");
flatpickr_instance = flatpickr($flatpickr_input[0], {
flatpickr_instance = flatpickr($flatpickr_input[0]!, {
mode: "single",
enableTime: true,
clickOpens: false,

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -225,10 +225,10 @@ export function create<T>(opts: InputPillCreateOptions<T>): InputPillContainer<T
const idx = store.pills.findIndex((pill) => 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<T>(opts: InputPillCreateOptions<T>): InputPillContainer<T
this.removeLastPill(quiet);
}
this.clear(store.$input[0]);
this.clear(store.$input[0]!);
},
insertManyPills(pills: string | string[]) {
@ -285,7 +285,7 @@ export function create<T>(opts: InputPillCreateOptions<T>): InputPillContainer<T
// when using the `text` insertion feature with jQuery the caret is
// placed at the beginning of the input field, so this moves it to
// the end.
ui_util.place_caret_at_end(store.$input[0]);
ui_util.place_caret_at_end(store.$input[0]!);
// this sends a flag if the operation wasn't completely successful,
// which in this case is defined as some of the pills not autofilling
@ -370,7 +370,7 @@ export function create<T>(opts: InputPillCreateOptions<T>): InputPillContainer<T
// if the pill is successful, it will create the pill and clear
// the input.
if (funcs.appendPill(store.$input.text().trim())) {
funcs.clear(store.$input[0]);
funcs.clear(store.$input[0]!);
}
e.preventDefault();
@ -399,7 +399,7 @@ export function create<T>(opts: InputPillCreateOptions<T>): InputPillContainer<T
break;
case "Backspace": {
const $next = $pill.next();
funcs.removePill($pill[0]);
funcs.removePill($pill[0]!);
$next.trigger("focus");
// the "Backspace" key in Firefox will go back a page if you do
// not prevent it.
@ -439,7 +439,7 @@ export function create<T>(opts: InputPillCreateOptions<T>): InputPillContainer<T
const $pill = $(this).closest(".pill");
const $next = $pill.next();
funcs.removePill($pill[0]);
funcs.removePill($pill[0]!);
$next.trigger("focus");
});

View File

@ -52,7 +52,9 @@ export function show_generate_integration_url_modal(api_key: string): void {
},
});
clipboard.on("success", () => {
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 () {

View File

@ -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 &&
$<HTMLInputElement>("input#invite_select_default_streams")[0].checked;
$<HTMLInputElement>("input#invite_select_default_streams")[0]!.checked;
if (hide_streams_list) {
$("#streams_to_add .invite-stream-controls").hide();
$("#invite-stream-checkboxes").hide();

View File

@ -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 {

View File

@ -29,7 +29,7 @@ function python_to_js_linkifier(
let current_group = 1;
const group_number_to_name: Record<number, string> = {};
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)) {

View File

@ -518,10 +518,10 @@ export function create<Key, Item = Key>(
}
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();

View File

@ -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);
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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;
},
});

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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"});

View File

@ -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);
}

View File

@ -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);
}
},

View File

@ -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.
},

View File

@ -192,7 +192,7 @@ export const default_popover_props: Partial<tippy.Props> = {
// $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<tippy.Props> = {
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

View File

@ -8,7 +8,7 @@ function get_new_rand(old_random_int: number, max: number): number {
function get_random_item_from_array<T>(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);

View File

@ -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 {

View File

@ -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) {

View File

@ -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) {

View File

@ -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");
});

View File

@ -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;

View File

@ -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<string, boolean> {
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<HTMLInputElement>("input")[0].checked;
new_auth_methods[method] = $(method_row).find<HTMLInputElement>("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<tippy.PopperElement>(
".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);
}

View File

@ -265,7 +265,7 @@ function show_modal(): void {
}
const formData = new FormData();
const files = $<HTMLInputElement>("input#emoji_file_input")[0].files;
const files = $<HTMLInputElement>("input#emoji_file_input")[0]!.files;
assert(files !== null);
for (const [i, file] of [...files].entries()) {
formData.append("file-" + i, file);

View File

@ -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",

View File

@ -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

View File

@ -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<HTMLInputElement>(
"input.new-realm-domain-allow-subdomains",
)[0].checked;
)[0]!.checked;
const data = {
domain,
allow_subdomains: JSON.stringify(allow_subdomains),

View File

@ -25,7 +25,7 @@ $(() => {
}
$.fn.get_offset_to_window = function () {
return this[0].getBoundingClientRect();
return this[0]!.getBoundingClientRect();
};
$.fn.expectOne = function () {

View File

@ -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.

View File

@ -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<HTMLElement>(hover_text_ids[i])!.style.display =
document.querySelector<HTMLElement>(hover_text_ids[i]!)!.style.display =
"inline";
document.querySelector<HTMLElement>(hover_value_ids[i])!.style.display =
document.querySelector<HTMLElement>(hover_value_ids[i]!)!.style.display =
"inline";
document.querySelector<HTMLElement>(hover_value_ids[i])!.textContent =
document.querySelector<HTMLElement>(hover_value_ids[i]!)!.textContent =
value.toString();
} else {
document.querySelector<HTMLElement>(hover_text_ids[i])!.style.display =
document.querySelector<HTMLElement>(hover_text_ids[i]!)!.style.display =
"none";
document.querySelector<HTMLElement>(hover_value_ids[i])!.style.display =
document.querySelector<HTMLElement>(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<number[]> = {human: [], bot: [], me: []};
let current: DataByUserType<number> = {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<Plotly.PlotlyHTMLElement>(
"#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<HTMLElement>(hover_value_ids[i])!.style.display =
document.querySelector<HTMLElement>(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<HTMLElement>(hover_value_ids[i])!.style.display =
document.querySelector<HTMLElement>(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<HTMLElement>(read_hover_text_ids[i])!.style.display =
"inline";
document.querySelector<HTMLElement>(
read_hover_value_ids[i],
read_hover_text_ids[i]!,
)!.style.display = "inline";
document.querySelector<HTMLElement>(read_hover_value_ids[i])!.textContent =
document.querySelector<HTMLElement>(
read_hover_value_ids[i]!,
)!.style.display = "inline";
document.querySelector<HTMLElement>(read_hover_value_ids[i]!)!.textContent =
value.toString();
} else {
document.querySelector<HTMLElement>(read_hover_text_ids[i])!.style.display =
"none";
document.querySelector<HTMLElement>(
read_hover_value_ids[i],
read_hover_text_ids[i]!,
)!.style.display = "none";
document.querySelector<HTMLElement>(
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<number[]> = {everyone: [], me: []};
let current: DataByEveryoneMe<number> = {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<Plotly.PlotlyHTMLElement>(
"#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;
}

View File

@ -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;

View File

@ -285,7 +285,7 @@ export function initialize(): void {
const second_line = $t({defaultMessage: "File name: {filename}"}, {filename});
$markup.append($("<br>"), $("<span>").text(second_line));
}
instance.setContent($markup[0]);
instance.setContent($markup[0]!);
return undefined;
},
onHidden(instance) {

View File

@ -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];
}
}

View File

@ -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 += "<strong>" + Handlebars.Utils.escapeExpression(piece) + "</strong>";

View File

@ -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;
});

View File

@ -35,7 +35,7 @@ export function lower_bound<T1, T2>(
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<T>(

View File

@ -38,7 +38,7 @@ export function eq_array<T>(
return false;
}
return a.every((item, i) => eq(item, b[i]));
return a.every((item, i) => eq(item, b[i]!));
}
export function ul<T>(opts: Options<T>): Tag<T> {
@ -198,7 +198,7 @@ export function update<T>(
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;
}