diff --git a/src/actions/shortcuts.ts b/src/actions/shortcuts.ts index ba66c5a7..be48c647 100644 --- a/src/actions/shortcuts.ts +++ b/src/actions/shortcuts.ts @@ -1,5 +1,6 @@ import { isDarwin } from "../constants"; import { t } from "../i18n"; +import { SubtypeOf } from "../utility-types"; import { getShortcutKey } from "../utils"; import { ActionName } from "./types"; diff --git a/src/actions/types.ts b/src/actions/types.ts index baa37eaa..e46cd2ab 100644 --- a/src/actions/types.ts +++ b/src/actions/types.ts @@ -6,6 +6,7 @@ import { ExcalidrawProps, BinaryFiles, } from "../types"; +import { MarkOptional } from "../utility-types"; export type ActionSource = "ui" | "keyboard" | "contextMenu" | "api"; diff --git a/src/data/blob.ts b/src/data/blob.ts index 473042b5..35c040ef 100644 --- a/src/data/blob.ts +++ b/src/data/blob.ts @@ -7,6 +7,7 @@ import { CanvasError } from "../errors"; import { t } from "../i18n"; import { calculateScrollCenter } from "../scene"; import { AppState, DataURL, LibraryItem } from "../types"; +import { ValueOf } from "../utility-types"; import { bytesToHexString } from "../utils"; import { FileSystemHandle, nativeFileSystemSupported } from "./filesystem"; import { isValidExcalidrawData, isValidLibrary } from "./json"; diff --git a/src/data/restore.ts b/src/data/restore.ts index 4434b7bc..98df4547 100644 --- a/src/data/restore.ts +++ b/src/data/restore.ts @@ -34,6 +34,7 @@ import { bumpVersion } from "../element/mutateElement"; import { getUpdatedTimestamp, updateActiveTool } from "../utils"; import { arrayToMap } from "../utils"; import oc from "open-color"; +import { MarkOptional, Mutable } from "../utility-types"; type RestoredAppState = Omit< AppState, diff --git a/src/element/bounds.ts b/src/element/bounds.ts index 2eab1d93..3245ca3f 100644 --- a/src/element/bounds.ts +++ b/src/element/bounds.ts @@ -23,6 +23,7 @@ import { import { rescalePoints } from "../points"; import { getBoundTextElement, getContainerElement } from "./textElement"; import { LinearElementEditor } from "./linearElementEditor"; +import { Mutable } from "../utility-types"; // x and y position of top left corner, x and y position of bottom right corner export type Bounds = readonly [number, number, number, number]; diff --git a/src/element/collision.ts b/src/element/collision.ts index 54540ae5..097b76d3 100644 --- a/src/element/collision.ts +++ b/src/element/collision.ts @@ -38,6 +38,7 @@ import { isTextElement } from "."; import { isTransparent } from "../utils"; import { shouldShowBoundingBox } from "./transformHandles"; import { getBoundTextElement } from "./textElement"; +import { Mutable } from "../utility-types"; const isElementDraggableFromInside = ( element: NonDeletedExcalidrawElement, diff --git a/src/element/linearElementEditor.ts b/src/element/linearElementEditor.ts index 5c478515..aedc2597 100644 --- a/src/element/linearElementEditor.ts +++ b/src/element/linearElementEditor.ts @@ -41,6 +41,7 @@ import { shouldRotateWithDiscreteAngle } from "../keys"; import { getBoundTextElement, handleBindTextResize } from "./textElement"; import { getShapeForElement } from "../renderer/renderElement"; import { DRAGGING_THRESHOLD } from "../constants"; +import { Mutable } from "../utility-types"; const editorMidPointsCache: { version: number | null; diff --git a/src/element/mutateElement.ts b/src/element/mutateElement.ts index 52038c16..1c3d6612 100644 --- a/src/element/mutateElement.ts +++ b/src/element/mutateElement.ts @@ -5,6 +5,7 @@ import { getSizeFromPoints } from "../points"; import { randomInteger } from "../random"; import { Point } from "../types"; import { getUpdatedTimestamp } from "../utils"; +import { Mutable } from "../utility-types"; type ElementUpdate = Omit< Partial, diff --git a/src/element/newElement.ts b/src/element/newElement.ts index 0c7c90d2..0fe16b55 100644 --- a/src/element/newElement.ts +++ b/src/element/newElement.ts @@ -32,6 +32,7 @@ import { } from "./textElement"; import { VERTICAL_ALIGN } from "../constants"; import { isArrowElement } from "./typeChecks"; +import { MarkOptional, Merge, Mutable } from "../utility-types"; type ElementConstructorOpts = MarkOptional< Omit, diff --git a/src/element/textElement.ts b/src/element/textElement.ts index a4c49479..18b96790 100644 --- a/src/element/textElement.ts +++ b/src/element/textElement.ts @@ -23,6 +23,7 @@ import { resetOriginalContainerCache, updateOriginalContainerCache, } from "./textWysiwyg"; +import { ExtractSetType } from "../utility-types"; export const normalizeText = (text: string) => { return ( diff --git a/src/element/typeChecks.ts b/src/element/typeChecks.ts index 0f13648f..164fafe6 100644 --- a/src/element/typeChecks.ts +++ b/src/element/typeChecks.ts @@ -1,5 +1,6 @@ import { ROUNDNESS } from "../constants"; import { AppState } from "../types"; +import { MarkNonNullable } from "../utility-types"; import { ExcalidrawElement, ExcalidrawTextElement, diff --git a/src/element/types.ts b/src/element/types.ts index af78f771..e99b7e89 100644 --- a/src/element/types.ts +++ b/src/element/types.ts @@ -6,6 +6,7 @@ import { THEME, VERTICAL_ALIGN, } from "../constants"; +import { MarkNonNullable, ValueOf } from "../utility-types"; export type ChartType = "bar" | "line"; export type FillStyle = "hachure" | "cross-hatch" | "solid"; diff --git a/src/excalidraw-app/data/firebase.ts b/src/excalidraw-app/data/firebase.ts index b6b26249..02e14466 100644 --- a/src/excalidraw-app/data/firebase.ts +++ b/src/excalidraw-app/data/firebase.ts @@ -14,6 +14,7 @@ import { encryptData, decryptData } from "../../data/encryption"; import { MIME_TYPES } from "../../constants"; import { reconcileElements } from "../collab/reconciliation"; import { getSyncableElements, SyncableExcalidrawElement } from "."; +import { ResolutionType } from "../../utility-types"; // private // ----------------------------------------------------------------------------- diff --git a/src/excalidraw-app/index.tsx b/src/excalidraw-app/index.tsx index 2b9101ad..1c51637e 100644 --- a/src/excalidraw-app/index.tsx +++ b/src/excalidraw-app/index.tsx @@ -85,6 +85,7 @@ import { useAtomWithInitialValue } from "../jotai"; import { appJotaiStore } from "./app-jotai"; import "./index.scss"; +import { ResolutionType } from "../utility-types"; polyfill(); diff --git a/src/global.d.ts b/src/global.d.ts index 4ccd8f3f..4a70443d 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -50,36 +50,6 @@ interface Clipboard extends EventTarget { write(data: any[]): Promise; } -type Mutable = { - -readonly [P in keyof T]: T[P]; -}; - -type ValueOf = T[keyof T]; - -type Merge = Omit & N; - -/** utility type to assert that the second type is a subtype of the first type. - * Returns the subtype. */ -type SubtypeOf = Subtype; - -type ResolutionType any> = T extends ( - ...args: any -) => Promise - ? R - : any; - -// https://github.com/krzkaczor/ts-essentials -type MarkOptional = Omit & Partial>; - -type MarkRequired = Exclude & - Required>; - -type MarkNonNullable = { - [P in K]-?: P extends K ? NonNullable : T[P]; -} & { [P in keyof T]: T[P] }; - -type NonOptional = Exclude; - // PNG encoding/decoding // ----------------------------------------------------------------------------- type TEXtChunk = { name: "tEXt"; data: Uint8Array }; @@ -101,23 +71,6 @@ declare module "png-chunks-extract" { } // ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- -// type getter for interface's callable type -// src: https://stackoverflow.com/a/58658851/927631 -// ----------------------------------------------------------------------------- -type SignatureType = T extends (...args: infer R) => any ? R : never; -type CallableType any> = ( - ...args: SignatureType -) => ReturnType; -// --------------------------------------------------------------------------— - -// Type for React.forwardRef --- supply only the first generic argument T -type ForwardRef = Parameters< - CallableType> ->[1]; - -// --------------------------------------------------------------------------— - interface Blob { handle?: import("browser-fs-acces").FileSystemHandle; name?: string; @@ -165,5 +118,3 @@ declare module "image-blob-reduce" { const reduce: ImageBlobReduce.ImageBlobReduceStatic; export = reduce; } - -type ExtractSetType> = T extends Set ? U : never; diff --git a/src/history.ts b/src/history.ts index cc620cae..d102a7ec 100644 --- a/src/history.ts +++ b/src/history.ts @@ -2,6 +2,7 @@ import { AppState } from "./types"; import { ExcalidrawElement } from "./element/types"; import { isLinearElement } from "./element/typeChecks"; import { deepCopyElement } from "./element/newElement"; +import { Mutable } from "./utility-types"; export interface HistoryEntry { appState: ReturnType; diff --git a/src/math.ts b/src/math.ts index cfa28e23..602fe976 100644 --- a/src/math.ts +++ b/src/math.ts @@ -12,6 +12,7 @@ import { } from "./element/types"; import { getShapeForElement } from "./renderer/renderElement"; import { getCurvePathOps } from "./element/bounds"; +import { Mutable } from "./utility-types"; export const rotate = ( x1: number, diff --git a/src/tests/helpers/api.ts b/src/tests/helpers/api.ts index 7f3e958c..a0feab2f 100644 --- a/src/tests/helpers/api.ts +++ b/src/tests/helpers/api.ts @@ -19,6 +19,7 @@ import { newFreeDrawElement, newImageElement } from "../../element/newElement"; import { Point } from "../../types"; import { getSelectedElements } from "../../scene/selection"; import { isLinearElementType } from "../../element/typeChecks"; +import { Mutable } from "../../utility-types"; const readFile = util.promisify(fs.readFile); diff --git a/src/types.ts b/src/types.ts index 486ec714..e40476ea 100644 --- a/src/types.ts +++ b/src/types.ts @@ -31,6 +31,7 @@ import Library from "./data/library"; import type { FileSystemHandle } from "./data/filesystem"; import type { ALLOWED_IMAGE_MIME_TYPES, MIME_TYPES } from "./constants"; import { ContextMenuItems } from "./components/ContextMenu"; +import { Merge, ForwardRef } from "./utility-types"; export type Point = Readonly; diff --git a/src/utility-types.ts b/src/utility-types.ts new file mode 100644 index 00000000..b84eb199 --- /dev/null +++ b/src/utility-types.ts @@ -0,0 +1,49 @@ +export type Mutable = { + -readonly [P in keyof T]: T[P]; +}; + +export type ValueOf = T[keyof T]; + +export type Merge = Omit & N; + +/** utility type to assert that the second type is a subtype of the first type. + * Returns the subtype. */ +export type SubtypeOf = Subtype; + +export type ResolutionType any> = T extends ( + ...args: any +) => Promise + ? R + : any; + +// https://github.com/krzkaczor/ts-essentials +export type MarkOptional = Omit & + Partial>; + +export type MarkRequired = Exclude & + Required>; + +export type MarkNonNullable = { + [P in K]-?: P extends K ? NonNullable : T[P]; +} & { [P in keyof T]: T[P] }; + +export type NonOptional = Exclude; + +// ----------------------------------------------------------------------------- +// type getter for interface's callable type +// src: https://stackoverflow.com/a/58658851/927631 +// ----------------------------------------------------------------------------- +export type SignatureType = T extends (...args: infer R) => any ? R : never; +export type CallableType any> = ( + ...args: SignatureType +) => ReturnType; +// --------------------------------------------------------------------------— + +// Type for React.forwardRef --- supply only the first generic argument T +export type ForwardRef = Parameters< + CallableType> +>[1]; + +export type ExtractSetType> = T extends Set + ? U + : never; diff --git a/src/utils.ts b/src/utils.ts index b2d85d4d..61925a60 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -16,6 +16,7 @@ import { AppState, DataURL, LastActiveTool, Zoom } from "./types"; import { unstable_batchedUpdates } from "react-dom"; import { SHAPES } from "./shapes"; import { isEraserActive, isHandToolActive } from "./appState"; +import { ResolutionType } from "./utility-types"; let mockDateTime: string | null = null;