@tldraw/utils
Version:
tldraw infinite canvas SDK (private utilities).
138 lines (127 loc) • 4.32 kB
text/typescript
/**
* Get whether a value is not undefined.
*
* @param value - The value to check.
* @returns True if the value is not undefined, with proper type narrowing.
* @example
* ```ts
* const maybeString: string | undefined = getValue()
*
* if (isDefined(maybeString)) {
* // TypeScript knows maybeString is string, not undefined
* console.log(maybeString.toUpperCase())
* }
*
* // Filter undefined values from arrays
* const values = [1, undefined, 2, undefined, 3]
* const definedValues = values.filter(isDefined) // [1, 2, 3]
* ```
* @public
*/
export function isDefined<T>(value: T): value is typeof value extends undefined ? never : T {
return value !== undefined
}
/**
* Get whether a value is not null.
*
* @param value - The value to check.
* @returns True if the value is not null, with proper type narrowing.
* @example
* ```ts
* const maybeString: string | null = getValue()
*
* if (isNonNull(maybeString)) {
* // TypeScript knows maybeString is string, not null
* console.log(maybeString.length)
* }
*
* // Filter null values from arrays
* const values = ["a", null, "b", null, "c"]
* const nonNullValues = values.filter(isNonNull) // ["a", "b", "c"]
* ```
* @public
*/
export function isNonNull<T>(value: T): value is typeof value extends null ? never : T {
return value !== null
}
/**
* Get whether a value is not nullish (not null and not undefined).
*
* @param value - The value to check.
* @returns True if the value is neither null nor undefined, with proper type narrowing.
* @example
* ```ts
* const maybeString: string | null | undefined = getValue()
*
* if (isNonNullish(maybeString)) {
* // TypeScript knows maybeString is string, not null or undefined
* console.log(maybeString.charAt(0))
* }
*
* // Filter nullish values from arrays
* const values = ["hello", null, "world", undefined, "!"]
* const cleanValues = values.filter(isNonNullish) // ["hello", "world", "!"]
* ```
* @public
*/
export function isNonNullish<T>(
value: T
): value is typeof value extends undefined ? never : typeof value extends null ? never : T {
return value !== null && value !== undefined
}
function getStructuredClone(): [<T>(i: T) => T, boolean] {
if (typeof globalThis !== 'undefined' && (globalThis as any).structuredClone) {
return [globalThis.structuredClone as <T>(i: T) => T, true]
}
if (typeof global !== 'undefined' && (global as any).structuredClone) {
return [global.structuredClone as <T>(i: T) => T, true]
}
if (typeof window !== 'undefined' && (window as any).structuredClone) {
return [window.structuredClone as <T>(i: T) => T, true]
}
return [<T>(i: T): T => (i ? JSON.parse(JSON.stringify(i)) : i), false]
}
const _structuredClone = getStructuredClone()
/**
* Create a deep copy of a value. Uses the structuredClone API if available, otherwise uses JSON.parse(JSON.stringify()).
*
* @param i - The value to clone.
* @returns A deep copy of the input value.
* @example
* ```ts
* const original = { a: 1, b: { c: 2 } }
* const copy = structuredClone(original)
*
* copy.b.c = 3
* console.log(original.b.c) // 2 (unchanged)
* console.log(copy.b.c) // 3
*
* // Works with complex objects
* const complexObject = {
* date: new Date(),
* array: [1, 2, 3],
* nested: { deep: { value: "test" } }
* }
* const cloned = structuredClone(complexObject)
* ```
* @public
*/
export const structuredClone = _structuredClone[0]
/**
* Whether the current environment has native structuredClone support.
* @returns True if using native structuredClone, false if using JSON fallback.
* @internal
*/
export const isNativeStructuredClone = _structuredClone[1]
/**
* The prototype object used by structuredClone for cloned objects.
* When we patch structuredClone in jsdom for testing (see https://github.com/jsdom/jsdom/issues/3363),
* the Object that is used as a prototype for the cloned object is not the same as the Object in
* the code under test (that comes from jsdom's fake global context). This constant is used in
* our code to work around this case.
*
* This is also the case for Array prototype, but that problem can be worked around with an
* Array.isArray() check.
* @internal
*/
export const STRUCTURED_CLONE_OBJECT_PROTOTYPE = Object.getPrototypeOf(structuredClone({}))