Skip to content

Commit

Permalink
Enable Typescript strictFunctionTypes (#32911)
Browse files Browse the repository at this point in the history
1. Enable
[strictFunctionTypes](https://www.typescriptlang.org/tsconfig/#strictFunctionTypes)
2. Introduce `DOMEvent` helper type which sets `e.target`. Surely not
totally correct with that `Partial` but seems to work.
3. Various type-related refactors, change objects in
`eventsource.sharedworker.ts` to `Map`.
  • Loading branch information
silverwind authored Dec 21, 2024
1 parent 09a0041 commit c0e80db
Show file tree
Hide file tree
Showing 21 changed files with 94 additions and 84 deletions.
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"verbatimModuleSyntax": true,
"stripInternal": true,
"strict": false,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noPropertyAccessFromIndexSignature": false,
Expand Down
5 changes: 2 additions & 3 deletions web_src/js/components/ContextPopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ const body = computed(() => {
const root = ref<HTMLElement | null>(null);
onMounted(() => {
root.value.addEventListener('ce-load-context-popup', (e: CustomEvent) => {
const data: IssuePathInfo = e.detail;
root.value.addEventListener('ce-load-context-popup', (e: CustomEventInit<IssuePathInfo>) => {
if (!loading.value && issue.value === null) {
load(data);
load(e.detail);
}
});
});
Expand Down
3 changes: 2 additions & 1 deletion web_src/js/features/clipboard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {showTemporaryTooltip} from '../modules/tippy.ts';
import {toAbsoluteUrl} from '../utils.ts';
import {clippie} from 'clippie';
import type {DOMEvent} from '../utils/dom.ts';

const {copy_success, copy_error} = window.config.i18n;

Expand All @@ -9,7 +10,7 @@ const {copy_success, copy_error} = window.config.i18n;
// - data-clipboard-target: Holds a selector for a <input> or <textarea> whose content is copied
// - data-clipboard-text-type: When set to 'url' will convert relative to absolute urls
export function initGlobalCopyToClipboardListener() {
document.addEventListener('click', async (e: MouseEvent & {target: HTMLElement}) => {
document.addEventListener('click', async (e: DOMEvent<MouseEvent>) => {
const target = e.target.closest('[data-clipboard-text], [data-clipboard-target]');
if (!target) return;

Expand Down
7 changes: 4 additions & 3 deletions web_src/js/features/colorpicker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {createTippy} from '../modules/tippy.ts';
import type {DOMEvent} from '../utils/dom.ts';

export async function initColorPickers() {
const els = document.querySelectorAll<HTMLElement>('.js-color-picker-input');
Expand Down Expand Up @@ -37,7 +38,7 @@ function initPicker(el: HTMLElement): void {
updateSquare(square, e.detail.value);
});

input.addEventListener('input', (e: Event & {target: HTMLInputElement}) => {
input.addEventListener('input', (e: DOMEvent<Event, HTMLInputElement>) => {
updateSquare(square, e.target.value);
updatePicker(picker, e.target.value);
});
Expand All @@ -55,8 +56,8 @@ function initPicker(el: HTMLElement): void {
});

// init precolors
for (const colorEl of el.querySelectorAll('.precolors .color')) {
colorEl.addEventListener('click', (e: MouseEvent & {target: HTMLAnchorElement}) => {
for (const colorEl of el.querySelectorAll<HTMLElement>('.precolors .color')) {
colorEl.addEventListener('click', (e: DOMEvent<MouseEvent, HTMLAnchorElement>) => {
const newValue = e.target.getAttribute('data-color-hex');
input.value = newValue;
input.dispatchEvent(new Event('input', {bubbles: true}));
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/features/common-form.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {applyAreYouSure, initAreYouSure} from '../vendor/jquery.are-you-sure.ts';
import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.ts';
import {queryElems} from '../utils/dom.ts';
import {queryElems, type DOMEvent} from '../utils/dom.ts';
import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';

export function initGlobalFormDirtyLeaveConfirm() {
Expand All @@ -13,7 +13,7 @@ export function initGlobalFormDirtyLeaveConfirm() {
}

export function initGlobalEnterQuickSubmit() {
document.addEventListener('keydown', (e: KeyboardEvent & {target: HTMLElement}) => {
document.addEventListener('keydown', (e: DOMEvent<KeyboardEvent>) => {
if (e.key !== 'Enter') return;
const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey);
if (hasCtrlOrMeta && e.target.matches('textarea')) {
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/features/comp/Cropper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {showElem} from '../../utils/dom.ts';
import {showElem, type DOMEvent} from '../../utils/dom.ts';

type CropperOpts = {
container: HTMLElement,
Expand Down Expand Up @@ -26,7 +26,7 @@ export async function initCompCropper({container, fileInput, imageSource}: Cropp
},
});

fileInput.addEventListener('input', (e: Event & {target: HTMLInputElement}) => {
fileInput.addEventListener('input', (e: DOMEvent<Event, HTMLInputElement>) => {
const files = e.target.files;
if (files?.length > 0) {
currentFileName = files[0].name;
Expand Down
5 changes: 3 additions & 2 deletions web_src/js/features/comp/ReactionSelector.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {POST} from '../../modules/fetch.ts';
import {fomanticQuery} from '../../modules/fomantic/base.ts';
import type {DOMEvent} from '../../utils/dom.ts';

export function initCompReactionSelector(parent: ParentNode = document) {
for (const container of parent.querySelectorAll('.issue-content, .diff-file-body')) {
container.addEventListener('click', async (e: MouseEvent & {target: HTMLElement}) => {
for (const container of parent.querySelectorAll<HTMLElement>('.issue-content, .diff-file-body')) {
container.addEventListener('click', async (e: DOMEvent<MouseEvent>) => {
// there are 2 places for the "reaction" buttons, one is the top-right reaction menu, one is the bottom of the comment
const target = e.target.closest('.comment-reaction-button');
if (!target) return;
Expand Down
49 changes: 25 additions & 24 deletions web_src/js/features/eventsource.sharedworker.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
const sourcesByUrl = {};
const sourcesByPort = {};

class Source {
url: string;
eventSource: EventSource;
listening: Record<string, any>;
clients: Array<any>;
listening: Record<string, boolean>;
clients: Array<MessagePort>;

constructor(url) {
constructor(url: string) {
this.url = url;
this.eventSource = new EventSource(url);
this.listening = {};
Expand All @@ -20,7 +17,7 @@ class Source {
this.listen('error');
}

register(port) {
register(port: MessagePort) {
if (this.clients.includes(port)) return;

this.clients.push(port);
Expand All @@ -31,7 +28,7 @@ class Source {
});
}

deregister(port) {
deregister(port: MessagePort) {
const portIdx = this.clients.indexOf(port);
if (portIdx < 0) {
return this.clients.length;
Expand All @@ -47,7 +44,7 @@ class Source {
this.eventSource = null;
}

listen(eventType) {
listen(eventType: string) {
if (this.listening[eventType]) return;
this.listening[eventType] = true;
this.eventSource.addEventListener(eventType, (event) => {
Expand All @@ -58,21 +55,25 @@ class Source {
});
}

notifyClients(event) {
notifyClients(event: {type: string, data: any}) {
for (const client of this.clients) {
client.postMessage(event);
}
}

status(port) {
status(port: MessagePort) {
port.postMessage({
type: 'status',
message: `url: ${this.url} readyState: ${this.eventSource.readyState}`,
});
}
}

self.addEventListener('connect', (e: Event & {ports: Array<any>}) => {
const sourcesByUrl: Map<string, Source | null> = new Map();
const sourcesByPort: Map<MessagePort, Source | null> = new Map();

// @ts-expect-error: typescript bug?
self.addEventListener('connect', (e: MessageEvent) => {
for (const port of e.ports) {
port.addEventListener('message', (event) => {
if (!self.EventSource) {
Expand All @@ -84,14 +85,14 @@ self.addEventListener('connect', (e: Event & {ports: Array<any>}) => {
}
if (event.data.type === 'start') {
const url = event.data.url;
if (sourcesByUrl[url]) {
if (sourcesByUrl.get(url)) {
// we have a Source registered to this url
const source = sourcesByUrl[url];
const source = sourcesByUrl.get(url);
source.register(port);
sourcesByPort[port] = source;
sourcesByPort.set(port, source);
return;
}
let source = sourcesByPort[port];
let source = sourcesByPort.get(port);
if (source) {
if (source.eventSource && source.url === url) return;

Expand All @@ -101,30 +102,30 @@ self.addEventListener('connect', (e: Event & {ports: Array<any>}) => {
// Clean-up
if (count === 0) {
source.close();
sourcesByUrl[source.url] = null;
sourcesByUrl.set(source.url, null);
}
}
// Create a new Source
source = new Source(url);
source.register(port);
sourcesByUrl[url] = source;
sourcesByPort[port] = source;
sourcesByUrl.set(url, source);
sourcesByPort.set(port, source);
} else if (event.data.type === 'listen') {
const source = sourcesByPort[port];
const source = sourcesByPort.get(port);
source.listen(event.data.eventType);
} else if (event.data.type === 'close') {
const source = sourcesByPort[port];
const source = sourcesByPort.get(port);

if (!source) return;

const count = source.deregister(port);
if (count === 0) {
source.close();
sourcesByUrl[source.url] = null;
sourcesByPort[port] = null;
sourcesByUrl.set(source.url, null);
sourcesByPort.set(port, null);
}
} else if (event.data.type === 'status') {
const source = sourcesByPort[port];
const source = sourcesByPort.get(port);
if (!source) {
port.postMessage({
type: 'status',
Expand Down
6 changes: 3 additions & 3 deletions web_src/js/features/notification.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import $ from 'jquery';
import {GET} from '../modules/fetch.ts';
import {toggleElem} from '../utils/dom.ts';
import {toggleElem, type DOMEvent} from '../utils/dom.ts';
import {logoutFromWorker} from '../modules/worker.ts';

const {appSubUrl, notificationSettings, assetVersionEncoded} = window.config;
Expand All @@ -25,8 +25,8 @@ export function initNotificationsTable() {
});

// mark clicked unread links for deletion on bfcache restore
for (const link of table.querySelectorAll('.notifications-item[data-status="1"] .notifications-link')) {
link.addEventListener('click', (e : MouseEvent & {target: HTMLElement}) => {
for (const link of table.querySelectorAll<HTMLAnchorElement>('.notifications-item[data-status="1"] .notifications-link')) {
link.addEventListener('click', (e: DOMEvent<MouseEvent>) => {
e.target.closest('.notifications-item').setAttribute('data-remove', 'true');
});
}
Expand Down
6 changes: 4 additions & 2 deletions web_src/js/features/oauth2-settings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type {DOMEvent} from '../utils/dom.ts';

export function initOAuth2SettingsDisableCheckbox() {
for (const el of document.querySelectorAll('.disable-setting')) {
el.addEventListener('change', (e: Event & {target: HTMLInputElement}) => {
for (const el of document.querySelectorAll<HTMLInputElement>('.disable-setting')) {
el.addEventListener('change', (e: DOMEvent<Event, HTMLInputElement>) => {
document.querySelector(e.target.getAttribute('data-target')).classList.toggle('disabled', e.target.checked);
});
}
Expand Down
12 changes: 6 additions & 6 deletions web_src/js/features/repo-graph.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {hideElem, showElem} from '../utils/dom.ts';
import {hideElem, showElem, type DOMEvent} from '../utils/dom.ts';
import {GET} from '../modules/fetch.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';

export function initRepoGraphGit() {
const graphContainer = document.querySelector('#git-graph-container');
const graphContainer = document.querySelector<HTMLElement>('#git-graph-container');
if (!graphContainer) return;

document.querySelector('#flow-color-monochrome')?.addEventListener('click', () => {
Expand Down Expand Up @@ -87,7 +87,7 @@ export function initRepoGraphGit() {
fomanticQuery(flowSelectRefsDropdown).dropdown({
clearable: true,
fullTextSeach: 'exact',
onRemove(toRemove) {
onRemove(toRemove: string) {
if (toRemove === '...flow-hide-pr-refs') {
params.delete('hide-pr-refs');
} else {
Expand All @@ -101,7 +101,7 @@ export function initRepoGraphGit() {
}
updateGraph();
},
onAdd(toAdd) {
onAdd(toAdd: string) {
if (toAdd === '...flow-hide-pr-refs') {
params.set('hide-pr-refs', 'true');
} else {
Expand All @@ -111,7 +111,7 @@ export function initRepoGraphGit() {
},
});

graphContainer.addEventListener('mouseenter', (e: MouseEvent & {target: HTMLElement}) => {
graphContainer.addEventListener('mouseenter', (e: DOMEvent<MouseEvent>) => {
if (e.target.matches('#rev-list li')) {
const flow = e.target.getAttribute('data-flow');
if (flow === '0') return;
Expand All @@ -132,7 +132,7 @@ export function initRepoGraphGit() {
}
});

graphContainer.addEventListener('mouseleave', (e: MouseEvent & {target: HTMLElement}) => {
graphContainer.addEventListener('mouseleave', (e: DOMEvent<MouseEvent>) => {
if (e.target.matches('#rev-list li')) {
const flow = e.target.getAttribute('data-flow');
if (flow === '0') return;
Expand Down
4 changes: 2 additions & 2 deletions web_src/js/features/repo-home.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {stripTags} from '../utils.ts';
import {hideElem, queryElemChildren, showElem} from '../utils/dom.ts';
import {hideElem, queryElemChildren, showElem, type DOMEvent} from '../utils/dom.ts';
import {POST} from '../modules/fetch.ts';
import {showErrorToast, type Toast} from '../modules/toast.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
Expand Down Expand Up @@ -28,7 +28,7 @@ export function initRepoTopicBar() {
mgrBtn.focus();
});

document.querySelector('#save_topic').addEventListener('click', async (e: MouseEvent & {target: HTMLButtonElement}) => {
document.querySelector<HTMLButtonElement>('#save_topic').addEventListener('click', async (e: DOMEvent<MouseEvent, HTMLButtonElement>) => {
lastErrorToast?.hideToast();
const topics = editDiv.querySelector<HTMLInputElement>('input[name=topics]').value;

Expand Down
Loading

0 comments on commit c0e80db

Please sign in to comment.