A bunch of reactive utility types and functions, for building primitives with Solid.js
—
Comprehensive TypeScript type utilities and definitions for enhanced type safety and developer experience. These types provide powerful abstractions for building type-safe applications with Solid.js and general TypeScript development.
Fundamental type utilities for common programming patterns.
/**
* Can be single or in an array
*/
type Many<T> = T | T[];
/**
* Get the values of an object type
*/
type Values<O extends Object> = O[keyof O];
/**
* Infers the type of the array elements
*/
type ItemsOf<T> = T extends (infer E)[] ? E : never;
/**
* Element type of Many<T>
*/
type ItemsOfMany<T> = T extends any[] ? ItemsOf<T> : T;
/**
* No operation function type
*/
type Noop = (...a: any[]) => void;Usage Examples:
import type { Many, Values, ItemsOf, ItemsOfMany } from "@solid-primitives/utils";
// Many type - accepts single value or array
const acceptMany = (input: Many<string>) => {
// Handle both "hello" and ["hello", "world"]
};
// Extract array element types
type StringArray = string[];
type ElementType = ItemsOf<StringArray>; // string
// Extract values from object
type User = { id: number; name: string; active: boolean };
type UserValues = Values<User>; // number | string | booleanTypes specifically designed for reactive programming and Solid.js integration.
/**
* T or a reactive/non-reactive function returning T
*/
type MaybeAccessor<T> = T | Accessor<T>;
/**
* Accessed value of a MaybeAccessor
*/
type MaybeAccessorValue<T extends MaybeAccessor<any>> = T extends () => any ? ReturnType<T> : T;
/**
* Solid.js Setter parameter type
*/
type SetterParam<T> = Parameters<Setter<T>>[0];
/**
* Directive type for Solid.js
*/
type Directive<P = true> = (el: Element, props: Accessor<P>) => void;
/**
* Effect function type for reactive computations
*/
type OnAccessEffectFunction<S, Prev, Next extends Prev = Prev> = (
input: AccessReturnTypes<S>,
prevInput: AccessReturnTypes<S>,
v: Prev
) => Next;
/**
* Return types of accessed values
*/
type AccessReturnTypes<S> = S extends MaybeAccessor<any>[]
? { [I in keyof S]: AccessReturnTypes<S[I]>; }
: MaybeAccessorValue<S>;Usage Examples:
import { createSignal } from "solid-js";
import type { MaybeAccessor, MaybeAccessorValue, Directive } from "@solid-primitives/utils";
// Function that accepts either value or accessor
const processValue = <T>(input: MaybeAccessor<T>): MaybeAccessorValue<T> => {
return typeof input === "function" ? input() : input;
};
// Custom directive type
const clickOutside: Directive<() => void> = (el, accessor) => {
const handler = (e: Event) => {
if (!el.contains(e.target as Node)) {
accessor()();
}
};
document.addEventListener("click", handler);
// ... cleanup logic
};Types for safe object manipulation and property access.
/**
* Allows to make shallow overwrites to an interface
*/
type Modify<T, R> = Omit<T, keyof R> & R;
/**
* Allows to make nested overwrites to an interface
*/
type ModifyDeep<A extends AnyObject, B extends DeepPartialAny<A>> = {
[K in keyof A]: B[K] extends never
? A[K]
: B[K] extends AnyObject
? ModifyDeep<A[K], B[K]>
: B[K];
} & (A extends AnyObject ? Omit<B, keyof A> : A);
/**
* Makes each property optional and turns each leaf property into any, allowing for type overrides
*/
type DeepPartialAny<T> = {
[P in keyof T]?: T[P] extends AnyObject ? DeepPartialAny<T[P]> : any;
};
/**
* Get the required keys of an object
*/
type RequiredKeys<T> = keyof {
[K in keyof T as T extends { [_ in K]: unknown } ? K : never]: 0;
};Usage Examples:
import type { Modify, ModifyDeep, RequiredKeys } from "@solid-primitives/utils";
// Modify interface
interface User {
id: number;
name: string;
email: string;
}
type AdminUser = Modify<User, { role: "admin"; permissions: string[] }>;
// Result: { id: number; name: string; email: string; role: "admin"; permissions: string[] }
// Deep modification
interface Config {
database: { host: string; port: number; };
api: { timeout: number; retries: number; };
}
type CustomConfig = ModifyDeep<Config, {
database: { ssl: boolean; };
api: { version: string; };
}>;
// Required keys extraction
interface PartialUser {
id: number;
name?: string;
email?: string;
}
type Required = RequiredKeys<PartialUser>; // "id"Advanced type manipulation utilities for complex scenarios.
/**
* Remove the first item of a tuple [1, 2, 3, 4] => [2, 3, 4]
*/
type Tail<T extends any[]> = ((...t: T) => void) extends (x: any, ...u: infer U) => void ? U : never;
/**
* Convert union to intersection: A | B => A & B
*/
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
/**
* Extract if possible, otherwise return U
*/
type ExtractIfPossible<T, U> = Extract<T, U> extends never ? U : Extract<T, U>;
/**
* Removes the [Symbol.iterator] functionality
*/
type NonIterable<T> = T & { [Symbol.iterator]: never };
/**
* Magic type that prevents inference at usage sites
*/
type NoInfer<T> = [T][T extends any ? 0 : never];Usage Examples:
import type { Tail, UnionToIntersection, NoInfer } from "@solid-primitives/utils";
// Remove first element from tuple
type OriginalTuple = [string, number, boolean];
type RestTuple = Tail<OriginalTuple>; // [number, boolean]
// Convert union to intersection
type Union = { a: string } | { b: number };
type Intersection = UnionToIntersection<Union>; // { a: string } & { b: number }
// Prevent type inference
const createWithDefault = <T>(value: T, defaultValue: NoInfer<T>): T => {
return value ?? defaultValue;
};Types for working with different categories of values.
/**
* Basic object types
*/
type AnyObject = Record<PropertyKey, any>;
type AnyStatic = [] | any[] | AnyObject;
type AnyFunction = (...args: any[]) => any;
type AnyClass = abstract new (...args: any) => any;
/**
* Primitive value types
*/
type PrimitiveValue = PropertyKey | boolean | bigint | null | undefined;
/**
* Falsy and truthy types
*/
type FalsyValue = false | 0 | "" | null | undefined;
type Truthy<T> = Exclude<T, FalsyValue>;
type Falsy<T> = Extract<T, FalsyValue>;Usage Examples:
import type { AnyObject, PrimitiveValue, Truthy, Falsy } from "@solid-primitives/utils";
// Generic object handling
const processObject = (obj: AnyObject) => {
// Handle any object type safely
};
// Filter truthy/falsy values
type MixedArray = (string | null | number | undefined | boolean)[];
type TruthyValues = Truthy<MixedArray[number]>; // string | number | true
type FalsyValues = Falsy<MixedArray[number]>; // null | undefined | falseTypes for representing geometric and user interface concepts.
/**
* 2D position coordinates
*/
type Position = {
x: number;
y: number;
};
/**
* Rectangular dimensions
*/
type Size = {
width: number;
height: number;
};Usage Examples:
import type { Position, Size } from "@solid-primitives/utils";
// Mouse position tracking
const [mousePos, setMousePos] = createSignal<Position>({ x: 0, y: 0 });
// Element dimensions
const [elementSize, setElementSize] = createSignal<Size>({ width: 0, height: 0 });
// Combined for element bounds
type Bounds = Position & Size;
const elementBounds: Bounds = {
x: 10,
y: 20,
width: 100,
height: 50
};Types for improving type readability and unwrapping complex types.
/**
* Unwraps the type definition of an object, making it more readable
*/
type Simplify<T> = T extends object ? { [K in keyof T]: T[K] } : T;
/**
* Unboxes type definition, making it more readable
*/
type UnboxLazy<T> = T extends () => infer U ? U : T;
/**
* Narrow type for better inference
*/
type Narrow<T> = T extends [] ? T : RawNarrow<T>;
type RawNarrow<T> =
| (T extends [] ? [] : never)
| (T extends string | number | bigint | boolean ? T : never)
| { [K in keyof T]: T[K] extends Function ? T[K] : RawNarrow<T[K]> };Usage Examples:
import type { Simplify, UnboxLazy, Narrow } from "@solid-primitives/utils";
// Simplify complex intersection types
type ComplexType = { a: string } & { b: number } & { c: boolean };
type SimpleType = Simplify<ComplexType>; // { a: string; b: number; c: boolean; }
// Unbox lazy types
type LazyString = () => string;
type UnboxedString = UnboxLazy<LazyString>; // string
// Better type narrowing
const narrowed = <T>(value: T): Narrow<T> => value as any;
const result = narrowed({ a: 1, b: "hello" }); // Properly narrowed typeThe package also re-exports some useful types from Solid.js:
// From solid-js
type EffectOptions = import("solid-js").EffectOptions;
type OnOptions = import("solid-js").OnOptions;
// Deprecated - from solid-js/types/reactive/signal.js
type ResolvedJSXElement = import("solid-js/types/reactive/signal.js").ResolvedJSXElement;
type ResolvedChildren = import("solid-js/types/reactive/signal.js").ResolvedChildren;Types from Solid.js that are referenced throughout the API:
/**
* Solid.js reactive accessor function
*/
type Accessor<T> = () => T;
/**
* Solid.js reactive setter function
*/
type Setter<T> = (value: T | ((prev: T) => T)) => T;
/**
* Options for createSignal
*/
interface SignalOptions<T> {
equals?: false | ((prev: T, next: T) => boolean);
internal?: boolean;
}
/**
* Array of accessor functions
*/
type AccessorArray<T> = readonly Accessor<T>[];
/**
* Effect function type
*/
type EffectFunction<Prev, Next = Prev> = (v: Prev) => Next;
/**
* Function that returns void
*/
type VoidFunction = () => void;Install with Tessl CLI
npx tessl i tessl/npm-solid-primitives--utils