tsconfig: Enable exactOptionalPropertyTypes.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2024-05-15 20:05:29 -07:00 committed by Tim Abbott
parent 08a50cf74b
commit 507eb4913c
33 changed files with 94 additions and 85 deletions

View File

@ -22,6 +22,7 @@
/* Strict type-checking */
"strict": true,
"exactOptionalPropertyTypes": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"noImplicitOverride": true,

View File

@ -13,7 +13,7 @@ export default class DebugRequirePlugin implements WebpackPluginInstance {
const resolved = new Map<string, Set<string>>();
const nameSymbol = Symbol("DebugRequirePluginName");
type NamedRequest = ResolveRequest & {
[nameSymbol]?: string;
[nameSymbol]?: string | undefined;
};
let debugRequirePath: string | false = false;

View File

@ -628,9 +628,9 @@ export async function run_test_async(test_function: (page: Page) => Promise<void
columnNumber,
}: ConsoleMessageLocation): Promise<string> => {
let frame = new StackFrame({
fileName: url,
lineNumber: lineNumber === undefined ? undefined : lineNumber + 1,
columnNumber: columnNumber === undefined ? undefined : columnNumber + 1,
...(url !== undefined && {fileName: url}),
...(lineNumber !== undefined && {lineNumber: lineNumber + 1}),
...(columnNumber !== undefined && {columnNumber: columnNumber + 1}),
});
try {
frame = await gps.getMappedLocation(frame);

View File

@ -28,11 +28,11 @@ type NumberedLine = {
};
type CleanStackFrame = {
full_path?: string;
show_path?: string;
function_name?: FunctionName;
line_number?: number;
context?: NumberedLine[];
full_path: string | undefined;
show_path: string | undefined;
function_name: FunctionName | undefined;
line_number: number | undefined;
context: NumberedLine[] | undefined;
};
export function exception_msg(

View File

@ -215,7 +215,7 @@ export class Typeahead<ItemType extends string | object> {
dropup: boolean;
automated: () => boolean;
trigger_selection: (event: JQuery.KeyDownEvent) => boolean;
on_escape?: () => void;
on_escape: (() => void) | undefined;
// returns a string to show in typeahead header or false.
header_html: () => string | false;
// returns a string to show in typeahead items or false.
@ -224,16 +224,16 @@ export class Typeahead<ItemType extends string | object> {
query = "";
mouse_moved_since_typeahead = false;
shown = false;
openInputFieldOnKeyUp?: () => void;
closeInputFieldOnHide?: () => void;
openInputFieldOnKeyUp: (() => void) | undefined;
closeInputFieldOnHide: (() => void) | undefined;
helpOnEmptyStrings: boolean;
tabIsEnter: boolean;
naturalSearch: boolean;
stopAdvance: boolean;
advanceKeyCodes: number[];
parentElement?: string;
parentElement: string | undefined;
values: WeakMap<HTMLElement, ItemType>;
instance?: Instance;
instance: Instance | undefined;
constructor(input_element: TypeaheadInputElement, options: TypeaheadOptions<ItemType>) {
this.input_element = input_element;

View File

@ -204,7 +204,7 @@ export function get_title_data(
is_group: boolean,
): {
first_line: string;
second_line?: string;
second_line: string | undefined;
third_line: string;
show_you?: boolean;
} {

View File

@ -14,17 +14,19 @@ import * as spectators from "./spectators";
type AjaxRequestHandlerOptions = Omit<JQuery.AjaxSettings, "success"> & {
url: string;
ignore_reload?: boolean;
success?: (
data: unknown,
textStatus: JQuery.Ajax.SuccessTextStatus,
jqXHR: JQuery.jqXHR<unknown>,
) => void;
success?:
| ((
data: unknown,
textStatus: JQuery.Ajax.SuccessTextStatus,
jqXHR: JQuery.jqXHR<unknown>,
) => void)
| undefined;
error?: JQuery.Ajax.ErrorCallback<unknown>;
};
type PatchRequestData =
| {processData: false; data: FormData}
| {processData?: true; data: Record<string, unknown>};
| {processData?: true | undefined; data: Record<string, unknown>};
export type AjaxRequestHandler = typeof call | typeof patch;

View File

@ -33,14 +33,14 @@ type ComposeActionsStartOpts = {
force_close?: boolean;
trigger?: string;
private_message_recipient?: string;
message?: Message;
stream_id?: number;
message?: Message | undefined;
stream_id?: number | undefined;
topic?: string;
content?: string;
draft_id?: string;
skip_scrolling_selected_message?: boolean;
is_reply?: boolean;
keep_composebox_empty?: boolean;
keep_composebox_empty?: boolean | undefined;
};
// An iteration on `ComposeActionsStartOpts` that enforces that

View File

@ -18,9 +18,9 @@ function format_stream_recipient_label(stream_id: number, topic: string): string
}
type ComposeClosedMessage = {
stream_id?: number;
stream_id?: number | undefined;
topic?: string;
display_reply_to?: string;
display_reply_to?: string | undefined;
};
export function get_recipient_label(message?: ComposeClosedMessage): string {

View File

@ -146,8 +146,8 @@ export function respond_to_message(opts: {
message_type: msg_type,
stream_id,
topic,
private_message_recipient: pm_recipient,
trigger: opts.trigger,
...(pm_recipient !== undefined && {private_message_recipient: pm_recipient}),
...(opts.trigger !== undefined && {trigger: opts.trigger}),
is_reply: true,
keep_composebox_empty: opts.keep_composebox_empty,
});

View File

@ -31,7 +31,7 @@ export type ComposeTriggeredOptions = {
| {
message_type: "stream";
topic: string;
stream_id?: number;
stream_id?: number | undefined;
}
| {
message_type: "private";

View File

@ -555,7 +555,7 @@ type FormattedDraft =
| {
is_stream: true;
draft_id: string;
stream_name?: string;
stream_name?: string | undefined;
recipient_bar_color: string;
stream_privacy_icon_color: string;
topic: string;

View File

@ -59,7 +59,7 @@ type DropdownWidgetOptions = {
focus_target_on_hidden?: boolean;
tippy_props?: Partial<tippy.Props>;
// NOTE: Any value other than `undefined` will be rendered when class is initialized.
default_id?: string | number;
default_id?: string | number | undefined;
unique_id_type?: DataTypes;
// Text to show if the current value is not in `get_options()`.
text_if_current_value_not_in_options?: string;

View File

@ -243,7 +243,7 @@ function message_matches_search_term(message: Message, operator: string, operand
export class Filter {
_terms: NarrowTerm[];
_sub?: StreamSubscription;
_sub?: StreamSubscription | undefined;
_sorted_term_types?: string[] = undefined;
_predicate?: (message: Message) => boolean;
_can_mark_messages_read?: boolean;
@ -914,8 +914,8 @@ export class Filter {
add_icon_data(context: {
title: string;
description?: string;
link?: string;
description?: string | undefined;
link?: string | undefined;
is_spectator: boolean;
}): IconData {
// We have special icons for the simple narrows available for the via sidebars.

View File

@ -860,7 +860,7 @@ function update_closed_compose_text($row: JQuery, is_header_row: boolean): void
compose_closed_ui.update_reply_recipient_label(message);
}
export function get_focused_row_message(): {message?: Message} & (
export function get_focused_row_message(): {message?: Message | undefined} & (
| {msg_type: "private"; private_message_recipient?: string}
| {msg_type: "stream"; stream_id: number; topic?: string}
| {msg_type?: never}
@ -893,6 +893,7 @@ export function get_focused_row_message(): {message?: Message} & (
const message = message_store.get(row_info.latest_msg_id);
if (message === undefined) {
const recipients = people.user_ids_string_to_emails_string(row_info.user_ids_string);
assert(recipients !== undefined);
return {
msg_type: "private",
private_message_recipient: recipients,

View File

@ -17,7 +17,7 @@ export type InputPillItem<T> = {
type: string;
img_src?: string;
deactivated?: boolean;
status_emoji_info?: EmojiRenderingDetails & {emoji_alt_code?: boolean}; // TODO: Move this in user_status.js
status_emoji_info?: (EmojiRenderingDetails & {emoji_alt_code?: boolean}) | undefined; // TODO: Move this in user_status.js
should_add_guest_user_indicator?: boolean;
} & T;
@ -58,11 +58,11 @@ type InputPillStore<T> = {
type InputPillRenderingDetails = {
display_value: string;
has_image: boolean;
img_src?: string;
deactivated?: boolean;
img_src?: string | undefined;
deactivated: boolean | undefined;
has_status?: boolean;
status_emoji_info?: EmojiRenderingDetails & {emoji_alt_code?: boolean};
should_add_guest_user_indicator?: boolean;
status_emoji_info?: (EmojiRenderingDetails & {emoji_alt_code?: boolean}) | undefined;
should_add_guest_user_indicator: boolean | undefined;
};
// These are the functions that are exposed to other modules.

View File

@ -14,7 +14,7 @@ type List<Key> = {
export class ListCursor<Key> {
highlight_class: string;
list: List<Key>;
curr_key?: Key;
curr_key?: Key | undefined;
constructor({highlight_class, list}: {highlight_class: string; list: List<Key>}) {
this.highlight_class = highlight_class;

View File

@ -11,7 +11,12 @@ export function make_indicator(
text,
width,
height,
}: {abs_positioned?: boolean; text?: string; width?: number; height?: number} = {},
}: {
abs_positioned?: boolean;
text?: string;
width?: number | undefined;
height?: number | undefined;
} = {},
): void {
let $container = $outer_container;

View File

@ -30,15 +30,15 @@ type EditHistoryEntry = {
edited_by_notice: string;
timestamp: number; // require to set data-message-id for overlay message row
is_stream: boolean;
recipient_bar_color?: string;
body_to_render?: string;
topic_edited?: boolean;
prev_topic?: string;
new_topic?: string;
stream_changed?: boolean;
prev_stream?: string;
prev_stream_id?: number;
new_stream?: string;
recipient_bar_color: string | undefined;
body_to_render: string | undefined;
topic_edited: boolean | undefined;
prev_topic: string | undefined;
new_topic: string | undefined;
stream_changed: boolean | undefined;
prev_stream: string | undefined;
prev_stream_id: number | undefined;
new_stream: string | undefined;
};
const server_message_history_schema = z.object({

View File

@ -7,8 +7,8 @@ import type {UserStatusEmojiInfo} from "./user_status";
const stored_messages = new Map<number, Message>();
export type MatchedMessage = {
match_content?: string;
match_subject?: string;
match_content?: string | undefined;
match_subject?: string | undefined;
};
export type MessageReactionType = "unicode_emoji" | "realm_emoji" | "zulip_extra_emoji";
@ -130,7 +130,7 @@ export type Message = (
flags?: string[];
small_avatar_url?: string; // Used in `message_avatar.hbs`
status_emoji_info?: UserStatusEmojiInfo; // Used in `message_body.hbs`
status_emoji_info?: UserStatusEmojiInfo | undefined; // Used in `message_body.hbs`
} & (
| {
type: "private";

View File

@ -10,9 +10,9 @@ type Hook = () => void;
export type ModalConfig = {
autoremove?: boolean;
on_show?: () => void;
on_shown?: () => void;
on_hide?: () => void;
on_hidden?: () => void;
on_shown?: (() => void) | undefined;
on_hide?: (() => void) | undefined;
on_hidden?: (() => void) | undefined;
};
const pre_open_hooks: Hook[] = [];

View File

@ -101,8 +101,9 @@ export function set_compose_defaults(): {
}
}
if (single.has("topic")) {
opts.topic = single.get("topic");
const topic = single.get("topic");
if (topic !== undefined) {
opts.topic = topic;
}
const private_message_recipient = single.get("dm");

View File

@ -20,7 +20,7 @@ import * as util from "./util";
export type ProfileData = {
value: string;
rendered_value?: string;
rendered_value?: string | undefined;
};
export type User = {
@ -29,7 +29,7 @@ export type User = {
email: string;
full_name: string;
// used for caching result of remove_diacritics.
name_with_diacritics_removed?: string;
name_with_diacritics_removed?: string | undefined;
date_joined: string;
is_active: boolean;
is_owner: boolean;
@ -1489,7 +1489,6 @@ export function make_user(user_id: number, email: string, full_name: string): Us
// We explicitly don't set `avatar_url` for fake person objects so that fallback code
// will ask the server or compute a gravatar URL only once we need the avatar URL,
// it's important for performance that we not hash every user's email to get gravatar URLs.
avatar_url: undefined,
avatar_version: 0,
timezone: "",
date_joined: "",

View File

@ -38,8 +38,8 @@ type DisplayObject = {
is_zero: boolean;
is_active: boolean;
url: string;
status_emoji_info?: UserStatusEmojiInfo;
user_circle_class?: string;
status_emoji_info: UserStatusEmojiInfo | undefined;
user_circle_class: string | undefined;
is_group: boolean;
is_bot: boolean;
};

View File

@ -19,8 +19,8 @@ import * as people from "./people";
export type Event = {sender_id: number; data: InboundData};
export type PollWidgetExtraData = {
question?: string;
options?: string[];
question?: string | undefined;
options?: string[] | undefined;
};
export function activate({

View File

@ -4,13 +4,13 @@ import {user_settings} from "./user_settings";
export type RawPresence = {
server_timestamp: number;
active_timestamp?: number;
idle_timestamp?: number;
active_timestamp?: number | undefined;
idle_timestamp?: number | undefined;
};
export type PresenceStatus = {
status: "active" | "idle" | "offline";
last_active?: number;
last_active?: number | undefined;
};
export type PresenceInfoFromEvent = {

View File

@ -39,7 +39,7 @@ export const invite_schema = z.intersection(
]),
);
type Invite = z.output<typeof invite_schema> & {
invited_as_text?: string;
invited_as_text?: string | undefined;
invited_absolute_time?: string;
expiry_date_absolute_time?: string;
is_admin?: boolean;

View File

@ -704,10 +704,10 @@ function populate_messages_sent_by_client(raw_data: unknown): void {
const layout: Partial<Plotly.Layout> = {
width: 750,
height: undefined, // set in draw_plot()
// height set in draw_plot()
margin: {l: 10, r: 10, b: 40, t: 10},
font: font_14pt,
xaxis: {range: undefined}, // set in draw_plot()
// xaxis set in draw_plot()
yaxis: {showticklabels: false},
showlegend: false,
};
@ -813,7 +813,7 @@ function populate_messages_sent_by_client(raw_data: unknown): void {
$("#id_messages_sent_by_client > div").removeClass("spinner");
const data_ = plot_data[user_button][time_button];
layout.height = layout.margin!.b! + data_.trace.x.length * 30;
layout.xaxis!.range = [0, Math.max(...data_.trace.x) * 1.3];
layout.xaxis = {range: [0, Math.max(...data_.trace.x) * 1.3]};
void Plotly.newPlot(
"id_messages_sent_by_client",
[data_.trace, data_.trace_annotations],

View File

@ -664,11 +664,11 @@ export function refresh_muted_or_unmuted_stream(sub: StreamSubscription): void {
}
export function get_sidebar_stream_topic_info(filter: Filter): {
stream_id?: number;
stream_id: number | undefined;
topic_selected: boolean;
} {
const result: {
stream_id?: number;
stream_id: number | undefined;
topic_selected: boolean;
} = {
stream_id: undefined,

View File

@ -141,7 +141,7 @@ export function spinner_li(): ListInfoNode {
}
export class TopicListWidget {
prior_dom?: vdom.Tag<ListInfoNodeOptions> = undefined;
prior_dom: vdom.Tag<ListInfoNodeOptions> | undefined = undefined;
$parent_elem: JQuery;
my_stream_id: number;

View File

@ -97,12 +97,12 @@ type StreamData = {
};
export function render_typeahead_item(args: {
primary?: string;
primary?: string | undefined;
is_person?: boolean;
img_src?: string;
status_emoji_info?: UserStatusEmojiInfo;
status_emoji_info?: UserStatusEmojiInfo | undefined;
secondary?: string | null;
pronouns?: string;
pronouns?: string | undefined;
is_user_group?: boolean;
stream?: StreamData;
is_unsubscribed?: boolean;
@ -443,10 +443,10 @@ export function sort_recipients<UserType extends UserOrMentionPillData | UserPil
}: {
users: UserType[];
query: string;
current_stream_id?: number;
current_topic?: string;
current_stream_id?: number | undefined;
current_topic?: string | undefined;
groups?: UserGroupPillData[];
max_num_items?: number;
max_num_items?: number | undefined;
}): (UserType | UserGroupPillData)[] {
function sort_relevance(items: UserType[]): UserType[] {
return sort_people_for_relevance(items, current_stream_id, current_topic);

View File

@ -74,7 +74,7 @@ export function extract_pm_recipients(recipients: string): string[] {
// When the type is "private", properties from to_user_ids might be undefined.
// See https://github.com/zulip/zulip/pull/23032#discussion_r1038480596.
export type Recipient =
| {type: "private"; to_user_ids?: string; reply_to: string}
| {type: "private"; to_user_ids?: string | undefined; reply_to: string}
| ({type: "stream"} & StreamTopic);
export const same_recipient = function util_same_recipient(a?: Recipient, b?: Recipient): boolean {

View File

@ -14,8 +14,8 @@ type ZFormExtraData = {
// TODO: This TodoWidgetExtraData type should be moved to web/src/todo_widget.js when it will be migrated
type TodoWidgetExtraData = {
task_list_title?: string;
tasks?: {task: string; desc: string}[];
task_list_title?: string | undefined;
tasks?: {task: string; desc: string}[] | undefined;
};
type WidgetExtraData = PollWidgetExtraData | TodoWidgetExtraData | ZFormExtraData | null;