Skip to content

Commit c0e80db

Browse files
authored
Enable Typescript strictFunctionTypes (#32911)
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`.
1 parent 09a0041 commit c0e80db

21 files changed

+94
-84
lines changed

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"verbatimModuleSyntax": true,
2323
"stripInternal": true,
2424
"strict": false,
25+
"strictFunctionTypes": true,
2526
"noUnusedLocals": true,
2627
"noUnusedParameters": true,
2728
"noPropertyAccessFromIndexSignature": false,

web_src/js/components/ContextPopup.vue

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@ const body = computed(() => {
2525
const root = ref<HTMLElement | null>(null);
2626
2727
onMounted(() => {
28-
root.value.addEventListener('ce-load-context-popup', (e: CustomEvent) => {
29-
const data: IssuePathInfo = e.detail;
28+
root.value.addEventListener('ce-load-context-popup', (e: CustomEventInit<IssuePathInfo>) => {
3029
if (!loading.value && issue.value === null) {
31-
load(data);
30+
load(e.detail);
3231
}
3332
});
3433
});

web_src/js/features/clipboard.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {showTemporaryTooltip} from '../modules/tippy.ts';
22
import {toAbsoluteUrl} from '../utils.ts';
33
import {clippie} from 'clippie';
4+
import type {DOMEvent} from '../utils/dom.ts';
45

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

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

web_src/js/features/colorpicker.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {createTippy} from '../modules/tippy.ts';
2+
import type {DOMEvent} from '../utils/dom.ts';
23

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

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

5758
// init precolors
58-
for (const colorEl of el.querySelectorAll('.precolors .color')) {
59-
colorEl.addEventListener('click', (e: MouseEvent & {target: HTMLAnchorElement}) => {
59+
for (const colorEl of el.querySelectorAll<HTMLElement>('.precolors .color')) {
60+
colorEl.addEventListener('click', (e: DOMEvent<MouseEvent, HTMLAnchorElement>) => {
6061
const newValue = e.target.getAttribute('data-color-hex');
6162
input.value = newValue;
6263
input.dispatchEvent(new Event('input', {bubbles: true}));

web_src/js/features/common-form.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {applyAreYouSure, initAreYouSure} from '../vendor/jquery.are-you-sure.ts';
22
import {handleGlobalEnterQuickSubmit} from './comp/QuickSubmit.ts';
3-
import {queryElems} from '../utils/dom.ts';
3+
import {queryElems, type DOMEvent} from '../utils/dom.ts';
44
import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.ts';
55

66
export function initGlobalFormDirtyLeaveConfirm() {
@@ -13,7 +13,7 @@ export function initGlobalFormDirtyLeaveConfirm() {
1313
}
1414

1515
export function initGlobalEnterQuickSubmit() {
16-
document.addEventListener('keydown', (e: KeyboardEvent & {target: HTMLElement}) => {
16+
document.addEventListener('keydown', (e: DOMEvent<KeyboardEvent>) => {
1717
if (e.key !== 'Enter') return;
1818
const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey);
1919
if (hasCtrlOrMeta && e.target.matches('textarea')) {

web_src/js/features/comp/Cropper.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {showElem} from '../../utils/dom.ts';
1+
import {showElem, type DOMEvent} from '../../utils/dom.ts';
22

33
type CropperOpts = {
44
container: HTMLElement,
@@ -26,7 +26,7 @@ export async function initCompCropper({container, fileInput, imageSource}: Cropp
2626
},
2727
});
2828

29-
fileInput.addEventListener('input', (e: Event & {target: HTMLInputElement}) => {
29+
fileInput.addEventListener('input', (e: DOMEvent<Event, HTMLInputElement>) => {
3030
const files = e.target.files;
3131
if (files?.length > 0) {
3232
currentFileName = files[0].name;

web_src/js/features/comp/ReactionSelector.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import {POST} from '../../modules/fetch.ts';
22
import {fomanticQuery} from '../../modules/fomantic/base.ts';
3+
import type {DOMEvent} from '../../utils/dom.ts';
34

45
export function initCompReactionSelector(parent: ParentNode = document) {
5-
for (const container of parent.querySelectorAll('.issue-content, .diff-file-body')) {
6-
container.addEventListener('click', async (e: MouseEvent & {target: HTMLElement}) => {
6+
for (const container of parent.querySelectorAll<HTMLElement>('.issue-content, .diff-file-body')) {
7+
container.addEventListener('click', async (e: DOMEvent<MouseEvent>) => {
78
// there are 2 places for the "reaction" buttons, one is the top-right reaction menu, one is the bottom of the comment
89
const target = e.target.closest('.comment-reaction-button');
910
if (!target) return;

web_src/js/features/eventsource.sharedworker.ts

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
const sourcesByUrl = {};
2-
const sourcesByPort = {};
3-
41
class Source {
52
url: string;
63
eventSource: EventSource;
7-
listening: Record<string, any>;
8-
clients: Array<any>;
4+
listening: Record<string, boolean>;
5+
clients: Array<MessagePort>;
96

10-
constructor(url) {
7+
constructor(url: string) {
118
this.url = url;
129
this.eventSource = new EventSource(url);
1310
this.listening = {};
@@ -20,7 +17,7 @@ class Source {
2017
this.listen('error');
2118
}
2219

23-
register(port) {
20+
register(port: MessagePort) {
2421
if (this.clients.includes(port)) return;
2522

2623
this.clients.push(port);
@@ -31,7 +28,7 @@ class Source {
3128
});
3229
}
3330

34-
deregister(port) {
31+
deregister(port: MessagePort) {
3532
const portIdx = this.clients.indexOf(port);
3633
if (portIdx < 0) {
3734
return this.clients.length;
@@ -47,7 +44,7 @@ class Source {
4744
this.eventSource = null;
4845
}
4946

50-
listen(eventType) {
47+
listen(eventType: string) {
5148
if (this.listening[eventType]) return;
5249
this.listening[eventType] = true;
5350
this.eventSource.addEventListener(eventType, (event) => {
@@ -58,21 +55,25 @@ class Source {
5855
});
5956
}
6057

61-
notifyClients(event) {
58+
notifyClients(event: {type: string, data: any}) {
6259
for (const client of this.clients) {
6360
client.postMessage(event);
6461
}
6562
}
6663

67-
status(port) {
64+
status(port: MessagePort) {
6865
port.postMessage({
6966
type: 'status',
7067
message: `url: ${this.url} readyState: ${this.eventSource.readyState}`,
7168
});
7269
}
7370
}
7471

75-
self.addEventListener('connect', (e: Event & {ports: Array<any>}) => {
72+
const sourcesByUrl: Map<string, Source | null> = new Map();
73+
const sourcesByPort: Map<MessagePort, Source | null> = new Map();
74+
75+
// @ts-expect-error: typescript bug?
76+
self.addEventListener('connect', (e: MessageEvent) => {
7677
for (const port of e.ports) {
7778
port.addEventListener('message', (event) => {
7879
if (!self.EventSource) {
@@ -84,14 +85,14 @@ self.addEventListener('connect', (e: Event & {ports: Array<any>}) => {
8485
}
8586
if (event.data.type === 'start') {
8687
const url = event.data.url;
87-
if (sourcesByUrl[url]) {
88+
if (sourcesByUrl.get(url)) {
8889
// we have a Source registered to this url
89-
const source = sourcesByUrl[url];
90+
const source = sourcesByUrl.get(url);
9091
source.register(port);
91-
sourcesByPort[port] = source;
92+
sourcesByPort.set(port, source);
9293
return;
9394
}
94-
let source = sourcesByPort[port];
95+
let source = sourcesByPort.get(port);
9596
if (source) {
9697
if (source.eventSource && source.url === url) return;
9798

@@ -101,30 +102,30 @@ self.addEventListener('connect', (e: Event & {ports: Array<any>}) => {
101102
// Clean-up
102103
if (count === 0) {
103104
source.close();
104-
sourcesByUrl[source.url] = null;
105+
sourcesByUrl.set(source.url, null);
105106
}
106107
}
107108
// Create a new Source
108109
source = new Source(url);
109110
source.register(port);
110-
sourcesByUrl[url] = source;
111-
sourcesByPort[port] = source;
111+
sourcesByUrl.set(url, source);
112+
sourcesByPort.set(port, source);
112113
} else if (event.data.type === 'listen') {
113-
const source = sourcesByPort[port];
114+
const source = sourcesByPort.get(port);
114115
source.listen(event.data.eventType);
115116
} else if (event.data.type === 'close') {
116-
const source = sourcesByPort[port];
117+
const source = sourcesByPort.get(port);
117118

118119
if (!source) return;
119120

120121
const count = source.deregister(port);
121122
if (count === 0) {
122123
source.close();
123-
sourcesByUrl[source.url] = null;
124-
sourcesByPort[port] = null;
124+
sourcesByUrl.set(source.url, null);
125+
sourcesByPort.set(port, null);
125126
}
126127
} else if (event.data.type === 'status') {
127-
const source = sourcesByPort[port];
128+
const source = sourcesByPort.get(port);
128129
if (!source) {
129130
port.postMessage({
130131
type: 'status',

web_src/js/features/notification.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import $ from 'jquery';
22
import {GET} from '../modules/fetch.ts';
3-
import {toggleElem} from '../utils/dom.ts';
3+
import {toggleElem, type DOMEvent} from '../utils/dom.ts';
44
import {logoutFromWorker} from '../modules/worker.ts';
55

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

2727
// mark clicked unread links for deletion on bfcache restore
28-
for (const link of table.querySelectorAll('.notifications-item[data-status="1"] .notifications-link')) {
29-
link.addEventListener('click', (e : MouseEvent & {target: HTMLElement}) => {
28+
for (const link of table.querySelectorAll<HTMLAnchorElement>('.notifications-item[data-status="1"] .notifications-link')) {
29+
link.addEventListener('click', (e: DOMEvent<MouseEvent>) => {
3030
e.target.closest('.notifications-item').setAttribute('data-remove', 'true');
3131
});
3232
}

web_src/js/features/oauth2-settings.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import type {DOMEvent} from '../utils/dom.ts';
2+
13
export function initOAuth2SettingsDisableCheckbox() {
2-
for (const el of document.querySelectorAll('.disable-setting')) {
3-
el.addEventListener('change', (e: Event & {target: HTMLInputElement}) => {
4+
for (const el of document.querySelectorAll<HTMLInputElement>('.disable-setting')) {
5+
el.addEventListener('change', (e: DOMEvent<Event, HTMLInputElement>) => {
46
document.querySelector(e.target.getAttribute('data-target')).classList.toggle('disabled', e.target.checked);
57
});
68
}

web_src/js/features/repo-graph.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {hideElem, showElem} from '../utils/dom.ts';
1+
import {hideElem, showElem, type DOMEvent} from '../utils/dom.ts';
22
import {GET} from '../modules/fetch.ts';
33
import {fomanticQuery} from '../modules/fomantic/base.ts';
44

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

99
document.querySelector('#flow-color-monochrome')?.addEventListener('click', () => {
@@ -87,7 +87,7 @@ export function initRepoGraphGit() {
8787
fomanticQuery(flowSelectRefsDropdown).dropdown({
8888
clearable: true,
8989
fullTextSeach: 'exact',
90-
onRemove(toRemove) {
90+
onRemove(toRemove: string) {
9191
if (toRemove === '...flow-hide-pr-refs') {
9292
params.delete('hide-pr-refs');
9393
} else {
@@ -101,7 +101,7 @@ export function initRepoGraphGit() {
101101
}
102102
updateGraph();
103103
},
104-
onAdd(toAdd) {
104+
onAdd(toAdd: string) {
105105
if (toAdd === '...flow-hide-pr-refs') {
106106
params.set('hide-pr-refs', 'true');
107107
} else {
@@ -111,7 +111,7 @@ export function initRepoGraphGit() {
111111
},
112112
});
113113

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

135-
graphContainer.addEventListener('mouseleave', (e: MouseEvent & {target: HTMLElement}) => {
135+
graphContainer.addEventListener('mouseleave', (e: DOMEvent<MouseEvent>) => {
136136
if (e.target.matches('#rev-list li')) {
137137
const flow = e.target.getAttribute('data-flow');
138138
if (flow === '0') return;

web_src/js/features/repo-home.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {stripTags} from '../utils.ts';
2-
import {hideElem, queryElemChildren, showElem} from '../utils/dom.ts';
2+
import {hideElem, queryElemChildren, showElem, type DOMEvent} from '../utils/dom.ts';
33
import {POST} from '../modules/fetch.ts';
44
import {showErrorToast, type Toast} from '../modules/toast.ts';
55
import {fomanticQuery} from '../modules/fomantic/base.ts';
@@ -28,7 +28,7 @@ export function initRepoTopicBar() {
2828
mgrBtn.focus();
2929
});
3030

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

0 commit comments

Comments
 (0)