/**
* @license
* Copyright 2022-2026 Matter.js Authors
* SPDX-License-Identifier: Apache-2.0
*/
export type Properties = { [key: string]: any };
/** Merges two types into one. */
export type Merge = {
[K in keyof A as K extends keyof B ? never : K]: A[K];
} & B;
export function Merge(a: A, b: B): Merge {
return { ...a, ...b };
}
/** Type that represents a class constructor of a defined type or extend of it */
export type ClassExtends = { new (...args: any[]): C };
/** Merge an array of objects into one. Currently assumes unique elements */
export type MergeAll = T extends [infer O extends Properties | undefined, ...infer R]
? O extends undefined
? MergeAll
: O & MergeAll
: T extends []
? {}
: never;
export function MergeAll(...objects: readonly [...T]): MergeAll {
return Object.assign({}, ...objects);
}
/** Pluck an item from an array of objects if present */
export type Pluck = T extends [infer O, ...infer R]
? K extends keyof O
? [O[K], ...Pluck]
: Pluck
: T extends []
? T
: never;
export function Pluck(
key: K,
...objects: readonly [...T]
): Pluck {
return objects.map(o => (o as any)[key]).filter(o => o !== undefined) as any;
}
/** Same as "a == undefined" but keeps the kids happy */
export function isNullish(a: any) {
return a === undefined || a === null;
}
export type MakeMandatory = Exclude;
/** Create a branded type */
declare const __brand: unique symbol;
// Don't think it should be necessary to export Brand but it will cause
// the following error under some circumstances:
//
// Exported variable 'XXX' has or is using name '__brand' from external
// module "../src/util/Type" but cannot be named.ts(4023)
//
// Specifically this occurs with the reference to Cluster.id in the "complete"
// cluster definitions
export type Brand = { [__brand]: B };
export type Branded = T & Brand;
/**
* Make a type immutable.
*
* TODO - might need to extend depending type (e.g. doesn't handle Maps, Sets or Promises yet)
*
* Good reference implementation here:
*
* https://github.com/ts-essentials/ts-essentials/blob/master/lib/deep-readonly/index.ts
*/
export type Immutable = T extends (...args: any[]) => any
? T
: T extends number // Necessary for our "branded" IDs
? T
: T extends bigint
? T
: T extends object
? { readonly [K in keyof T]: Immutable }
: T;
export type Mutable = T extends (...args: any[]) => any
? T
: T extends number // Necessary for our "branded" IDs
? T
: T extends bigint
? T
: T extends object
? { -readonly [K in keyof T]: Mutable }
: T;
export function Mutable(value: Immutable): Mutable {
return value as Mutable;
}
/**
* Convert a union to an interface.
*
* @see {@link https://stackoverflow.com/questions/50374908}
*/
export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
/**
* An identity type.
*
* You can't do:
*
* interface Foo extends typeof Bar {}
*
* But you can do:
*
* interface Foo extends Identity {}
*
* Without this type you'd have to do:
*
* interface FooType = typeof Bar;
* interface Foo extends FooType {};
*
* We have to do this a lot because we generate complex objects with detailed
* type information. When exported, TS (as of 5.2) inlines the type of these
* objects in declarations which makes our declarations massive. To avoid this
* we create an interface from the type then cast to the interface for export.
*/
export type Identity = T;
/**
* Tests whether the given variable is a real object and not an Array
* @param it The variable to test
* @returns true if it is Record
*/
export function isObject(it: unknown): it is Record {
// This is necessary because:
// typeof null === 'object'
// typeof [] === 'object'
// [] instanceof Object === true
return Object.prototype.toString.call(it) === "[object Object]"; // this code is 25% faster than below one
// return it && typeof it === 'object' && !(it instanceof Array);
}
/**
* Tests whether two types are exactly equal.
*/
export type IfEquals =
(() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? A : B;
/**
* Extract keys of writable (non-readonly) properties from an interface.
*/
export type WritableKeys = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>;
}[keyof T];
/**
* Extract keys of readonly properties from an interface.
*/
export type ReadonlyKeys = Exclude>;
/**
* Extract keys of required (non-optional) properties from an interface.
*/
export type RequiredKeys = { [K in keyof T]-?: {} extends Pick ? never : K }[keyof T];
/**
* Extract keys of optional properties from an interface.
*/
export type OptionalKeys = Exclude>;
/**
* Recursive deep-partial type with array index key support.
*
* Primitives and functions pass through unchanged; objects become deeply optional; arrays accept either a partial array
* or an object keyed by numeric string indices.
*/
export type DeepPartial = V extends (infer E)[]
? { readonly [K in `${number}`]: DeepPartial } | Readonly[]>
: V extends boolean | number | bigint | string
? V
: V extends object
? V extends (...args: any[]) => any
? never
: {
readonly [K in keyof V]?: DeepPartial;
}
: V;