소스 검색

ref: add return type annotations

Sv443 10 달 전
부모
커밋
365ca960e2
7개의 변경된 파일53개의 추가작업 그리고 43개의 파일을 삭제
  1. 5 5
      lib/DataStore.ts
  2. 11 11
      lib/SelectorObserver.ts
  3. 3 3
      lib/array.ts
  4. 15 12
      lib/dom.ts
  5. 3 3
      lib/math.ts
  6. 12 5
      lib/misc.ts
  7. 4 4
      lib/translation.ts

+ 5 - 5
lib/DataStore.ts

@@ -135,7 +135,7 @@ export class DataStore<TData = any> {
   }
 
   /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
-  public setData(data: TData) {
+  public setData(data: TData): Promise<void> {
     this.cachedData = data;
     const useEncoding = Boolean(this.encodeData && this.decodeData);
     return new Promise<void>(async (resolve) => {
@@ -149,7 +149,7 @@ export class DataStore<TData = any> {
   }
 
   /** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
-  public async saveDefaultData() {
+  public async saveDefaultData(): Promise<void> {
     this.cachedData = this.defaultData;
     const useEncoding = Boolean(this.encodeData && this.decodeData);
     return new Promise<void>(async (resolve) => {
@@ -169,7 +169,7 @@ export class DataStore<TData = any> {
    *   
    * ⚠️ This requires the additional directive `@grant GM.deleteValue`
    */
-  public async deleteData() {
+  public async deleteData(): Promise<void> {
     await Promise.all([
       GM.deleteValue(`_uucfg-${this.id}`),
       GM.deleteValue(`_uucfgver-${this.id}`),
@@ -215,7 +215,7 @@ export class DataStore<TData = any> {
   }
 
   /** Serializes the data using the optional this.encodeData() and returns it as a string */
-  private async serializeData(data: TData, useEncoding = true) {
+  private async serializeData(data: TData, useEncoding = true): Promise<string> {
     const stringData = JSON.stringify(data);
     if(!this.encodeData || !this.decodeData || !useEncoding)
       return stringData;
@@ -227,7 +227,7 @@ export class DataStore<TData = any> {
   }
 
   /** Deserializes the data using the optional this.decodeData() and returns it as a JSON object */
-  private async deserializeData(data: string, useEncoding = true) {
+  private async deserializeData(data: string, useEncoding = true): Promise<TData> {
     let decRes = this.decodeData && this.encodeData && useEncoding ? this.decodeData(data) : undefined;
     if(decRes instanceof Promise)
       decRes = await decRes;

+ 11 - 11
lib/SelectorObserver.ts

@@ -92,12 +92,12 @@ export class SelectorObserver {
     };
   }
 
-  private checkAllSelectors() {
+  private checkAllSelectors(): void {
     for(const [selector, listeners] of this.listenerMap.entries())
       this.checkSelector(selector, listeners);
   }
 
-  private checkSelector(selector: string, listeners: SelectorListenerOptions[]) {
+  private checkSelector(selector: string, listeners: SelectorListenerOptions[]): void {
     if(!this.enabled)
       return;
 
@@ -147,7 +147,7 @@ export class SelectorObserver {
    * @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
    * @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
    */
-  public addListener<TElem extends Element = HTMLElement>(selector: string, options: SelectorListenerOptions<TElem>) {
+  public addListener<TElem extends Element = HTMLElement>(selector: string, options: SelectorListenerOptions<TElem>): void {
     options = { all: false, continuous: false, debounce: 0, ...options };
     if((options.debounce && options.debounce > 0) || (this.customOptions.defaultDebounce && this.customOptions.defaultDebounce > 0)) {
       options.listener = this.debounce(
@@ -168,7 +168,7 @@ export class SelectorObserver {
   }
 
   /** Disables the observation of the child elements */
-  public disable() {
+  public disable(): void {
     if(!this.enabled)
       return;
     this.enabled = false;
@@ -180,7 +180,7 @@ export class SelectorObserver {
    * @param immediatelyCheckSelectors Whether to immediately check if all previously registered selectors exist (default is true)
    * @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
    */
-  public enable(immediatelyCheckSelectors = true) {
+  public enable(immediatelyCheckSelectors = true): boolean {
     const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
     if(this.enabled || !baseElement)
       return false;
@@ -192,12 +192,12 @@ export class SelectorObserver {
   }
 
   /** Returns whether the observation of the child elements is currently enabled */
-  public isEnabled() {
+  public isEnabled(): boolean {
     return this.enabled;
   }
 
   /** Removes all listeners that have been registered with {@linkcode addListener()} */
-  public clearListeners() {
+  public clearListeners(): void {
     this.listenerMap.clear();
   }
 
@@ -205,7 +205,7 @@ export class SelectorObserver {
    * Removes all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()}
    * @returns Returns true when all listeners for the associated selector were found and removed, false otherwise
    */
-  public removeAllListeners(selector: string) {
+  public removeAllListeners(selector: string): boolean {
     return this.listenerMap.delete(selector);
   }
 
@@ -213,7 +213,7 @@ export class SelectorObserver {
    * Removes a single listener for the given {@linkcode selector} and {@linkcode options} that has been registered with {@linkcode addListener()}
    * @returns Returns true when the listener was found and removed, false otherwise
    */
-  public removeListener(selector: string, options: SelectorListenerOptions) {
+  public removeListener(selector: string, options: SelectorListenerOptions): boolean {
     const listeners = this.listenerMap.get(selector);
     if(!listeners)
       return false;
@@ -226,12 +226,12 @@ export class SelectorObserver {
   }
 
   /** Returns all listeners that have been registered with {@linkcode addListener()} */
-  public getAllListeners() {
+  public getAllListeners(): Map<string, SelectorListenerOptions<HTMLElement>[]> {
     return this.listenerMap;
   }
 
   /** Returns all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} */
-  public getListeners(selector: string) {
+  public getListeners(selector: string): SelectorListenerOptions<HTMLElement>[] | undefined {
     return this.listenerMap.get(selector);
   }
 }

+ 3 - 3
lib/array.ts

@@ -4,7 +4,7 @@ import { randRange } from "./math";
 export type NonEmptyArray<TArray = unknown> = [TArray, ...TArray[]];
 
 /** Returns a random item from the passed array */
-export function randomItem<TItem = unknown>(array: TItem[]) {
+export function randomItem<TItem = unknown>(array: TItem[]): TItem | undefined {
   return randomItemIndex<TItem>(array)[0];
 }
 
@@ -22,7 +22,7 @@ export function randomItemIndex<TItem = unknown>(array: TItem[]): [item?: TItem,
 }
 
 /** Returns a random item from the passed array and mutates the array to remove the item */
-export function takeRandomItem<TItem = unknown>(arr: TItem[]) {
+export function takeRandomItem<TItem = unknown>(arr: TItem[]): TItem | undefined {
   const [itm, idx] = randomItemIndex<TItem>(arr);
 
   if(idx === undefined)
@@ -33,7 +33,7 @@ export function takeRandomItem<TItem = unknown>(arr: TItem[]) {
 }
 
 /** Returns a copy of the array with its items in a random order */
-export function randomizeArray<TItem = unknown>(array: TItem[]) {
+export function randomizeArray<TItem = unknown>(array: TItem[]): TItem[] {
   const retArray = [...array]; // so array and retArray don't point to the same memory address
 
   if(array.length === 0)

+ 15 - 12
lib/dom.ts

@@ -1,7 +1,7 @@
 /**
  * Returns `unsafeWindow` if the `@grant unsafeWindow` is given, otherwise falls back to the regular `window`
  */
-export function getUnsafeWindow() {
+export function getUnsafeWindow(): Window {
   try {
     // throws ReferenceError if the "@grant unsafeWindow" isn't present
     return unsafeWindow;
@@ -15,7 +15,7 @@ export function getUnsafeWindow() {
  * Adds a parent container around the provided element
  * @returns Returns the new parent element
  */
-export function addParent(element: Element, newParent: Element) {
+export function addParent<TElem extends Element, TParentElem extends Element>(element: TElem, newParent: TParentElem): TParentElem {
   const oldParent = element.parentNode;
 
   if(!oldParent)
@@ -33,7 +33,7 @@ export function addParent(element: Element, newParent: Element) {
  * @param style CSS string
  * @returns Returns the created style element
  */
-export function addGlobalStyle(style: string) {
+export function addGlobalStyle(style: string): HTMLStyleElement {
   const styleElem = document.createElement("style");
   styleElem.innerHTML = style;
   document.head.appendChild(styleElem);
@@ -45,8 +45,8 @@ export function addGlobalStyle(style: string) {
  * @param rejects If set to `true`, the returned PromiseSettledResults will contain rejections for any of the images that failed to load
  * @returns Returns an array of `PromiseSettledResult` - each resolved result will contain the loaded image element, while each rejected result will contain an `ErrorEvent`
  */
-export function preloadImages(srcUrls: string[], rejects = false) {
-  const promises = srcUrls.map(src => new Promise((res, rej) => {
+export function preloadImages(srcUrls: string[], rejects = false): Promise<PromiseSettledResult<HTMLImageElement>[]> {
+  const promises = srcUrls.map(src => new Promise<HTMLImageElement>((res, rej) => {
     const image = new Image();
     image.src = src;
     image.addEventListener("load", () => res(image));
@@ -89,11 +89,14 @@ export function openInNewTab(href: string, background?: boolean) {
  * This function should be called as soon as possible (I recommend using `@run-at document-start`), as it will only intercept events that are added after this function is called.  
  * Calling this function will set `Error.stackTraceLimit = 100` (if not already higher) to ensure the stack trace is preserved.
  */
-export function interceptEvent<TEvtObj extends EventTarget, TPredicateEvt extends Event>(
+export function interceptEvent<
+  TEvtObj extends EventTarget,
+  TPredicateEvt extends Event
+> (
   eventObject: TEvtObj,
   eventName: Parameters<TEvtObj["addEventListener"]>[0],
   predicate: (event: TPredicateEvt) => boolean = () => true,
-) {
+): void {
   // default is 25 on FF so this should hopefully be more than enough
   // @ts-ignore
   Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
@@ -125,12 +128,12 @@ export function interceptEvent<TEvtObj extends EventTarget, TPredicateEvt extend
 export function interceptWindowEvent<TEvtKey extends keyof WindowEventMap>(
   eventName: TEvtKey,
   predicate: (event: WindowEventMap[TEvtKey]) => boolean = () => true,
-) {  
+): void {  
   return interceptEvent(getUnsafeWindow(), eventName, predicate);
 }
 
 /** Checks if an element is scrollable in the horizontal and vertical directions */
-export function isScrollable(element: Element) {
+export function isScrollable(element: Element): Record<"vertical" | "horizontal", boolean> {
   const { overflowX, overflowY } = getComputedStyle(element);
   return {
     vertical: (overflowY === "scroll" || overflowY === "auto") && element.scrollHeight > element.clientHeight,
@@ -150,11 +153,11 @@ export function isScrollable(element: Element) {
 export function observeElementProp<
   TElem extends Element = HTMLElement,
   TPropKey extends keyof TElem = keyof TElem,
->(
+> (
   element: TElem,
   property: TPropKey,
   callback: (oldVal: TElem[TPropKey], newVal: TElem[TPropKey]) => void
-) {
+): void {
   const elementPrototype = Object.getPrototypeOf(element);
   // eslint-disable-next-line no-prototype-builtins
   if(elementPrototype.hasOwnProperty(property)) {
@@ -192,7 +195,7 @@ export function observeElementProp<
  */
 export function getSiblingsFrame<
   TSibling extends Element = HTMLElement,
->(
+> (
   refElement: Element,
   siblingAmount: number,
   refElementAlignment: "center-top" | "center-bottom" | "top" | "bottom" = "center-top",

+ 3 - 3
lib/math.ts

@@ -1,5 +1,5 @@
 /** Ensures the passed {@linkcode value} always stays between {@linkcode min} and {@linkcode max} */
-export function clamp(value: number, min: number, max: number) {
+export function clamp(value: number, min: number, max: number): number {
   return Math.max(Math.min(value, max), min);
 }
 
@@ -7,7 +7,7 @@ export function clamp(value: number, min: number, max: number) {
  * Transforms the value parameter from the numerical range `range1min─range1max` to the numerical range `range2min─range2max`  
  * For example, you can map the value 2 in the range of 0-5 to the range of 0-10 and you'd get a 4 as a result.
  */
-export function mapRange(value: number, range1min: number, range1max: number, range2min: number, range2max: number) {
+export function mapRange(value: number, range1min: number, range1max: number, range2min: number, range2max: number): number {
   if(Number(range1min) === 0.0 && Number(range2min) === 0.0)
     return value * (range2max / range1max);
 
@@ -52,7 +52,7 @@ export function randRange(...args: number[]): number {
  * @param length The length of the ID to generate (defaults to 16)
  * @param radix The [radix](https://en.wikipedia.org/wiki/Radix) of each digit (defaults to 16 which is hexadecimal. Use 2 for binary, 10 for decimal, 36 for alphanumeric, etc.)
  */
-export function randomId(length = 16, radix = 16) {
+export function randomId(length = 16, radix = 16): string {
   const arr = new Uint8Array(length);
   crypto.getRandomValues(arr);
   return Array.from(

+ 12 - 5
lib/misc.ts

@@ -6,14 +6,14 @@ import type { Stringifiable } from "./types";
  * @param word A word in singular form, to auto-convert to plural
  * @param num If this is an array or NodeList, the amount of items is used
  */
-export function autoPlural(word: Stringifiable, num: number | unknown[] | NodeList) {
+export function autoPlural(word: Stringifiable, num: number | unknown[] | NodeList): string {
   if(Array.isArray(num) || num instanceof NodeList)
     num = num.length;
   return `${word}${num === 1 ? "" : "s"}`;
 }
 
 /** Pauses async execution for the specified time in ms */
-export function pauseFor(time: number) {
+export function pauseFor(time: number): Promise<void> {
   return new Promise<void>((res) => {
     setTimeout(() => res(), time);
   });
@@ -26,7 +26,14 @@ export function pauseFor(time: number) {
  * @param timeout The time in ms to wait before calling the function
  * @param edge Whether to call the function at the very first call ("rising" edge) or the very last call ("falling" edge, default)
  */
-export function debounce<TFunc extends (...args: TArgs[]) => void, TArgs = any>(func: TFunc, timeout = 300, edge: "rising" | "falling" = "falling") { // eslint-disable-line @typescript-eslint/no-explicit-any
+export function debounce<
+  TFunc extends (...args: TArgs[]) => void, // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  TArgs = any,
+> (
+  func: TFunc,
+  timeout = 300,
+  edge: "rising" | "falling" = "falling"
+): (...args: TArgs[]) => void {
   let timer: NodeJS.Timeout | undefined;
 
   return function(...args: TArgs[]) {
@@ -53,7 +60,7 @@ export type FetchAdvancedOpts = Omit<
 >;
 
 /** Calls the fetch API with special options like a timeout */
-export async function fetchAdvanced(input: RequestInfo | URL, options: FetchAdvancedOpts = {}) {
+export async function fetchAdvanced(input: RequestInfo | URL, options: FetchAdvancedOpts = {}): Promise<Response> {
   const { timeout = 10000 } = options;
 
   let signalOpts: Partial<RequestInit> = {},
@@ -80,7 +87,7 @@ export async function fetchAdvanced(input: RequestInfo | URL, options: FetchAdva
  * @param input The string to insert the values into
  * @param values The values to insert, in order, starting at `%1`
  */
-export function insertValues(input: string, ...values: Stringifiable[]) {
+export function insertValues(input: string, ...values: Stringifiable[]): string {
   return input.replace(/%\d/gm, (match) => {
     const argIndex = Number(match.substring(1)) - 1;
     return (values[argIndex] ?? match)?.toString();

+ 4 - 4
lib/translation.ts

@@ -13,7 +13,7 @@ let curLang: string;
  * @param key Key of the translation to return
  * @param args Optional arguments to be passed to the translated text. They will replace placeholders in the format `%n`, where `n` is the 1-indexed argument number
  */
-function tr(key: string, ...args: Stringifiable[]) {
+function tr(key: string, ...args: Stringifiable[]): string {
   if(!curLang)
     return key;
   const trText = trans[curLang]?.[key];
@@ -26,15 +26,15 @@ function tr(key: string, ...args: Stringifiable[]) {
   return trText;
 }
 
-tr.addLanguage = (language: string, translations: Record<string, string>) => {
+tr.addLanguage = (language: string, translations: Record<string, string>): void => {
   trans[language] = translations;
 };
 
-tr.setLanguage = (language: string) => {
+tr.setLanguage = (language: string): void => {
   curLang = language;
 };
 
-tr.getLanguage = () => {
+tr.getLanguage = (): string => {
   return curLang;
 };