@knyt/luthier
Version:
A library for building standardized, type-safe native web components with full SSR and hydration support.
211 lines • 9.58 kB
TypeScript
import type { ToAttributeValue, ToPropertyValue } from "@knyt/artisan";
import type { AnyProps, AttributeDictionary, ElementBuilder, InferElementProps, View } from "@knyt/weaver";
import type { KnytElement } from "./KnytElement";
export type PropertyName = string | symbol;
export type AttributeName = string | undefined | false;
export type ContainerRefValue = HTMLElement | DocumentFragment | ShadowRoot | null;
export type ReactiveProperty = PropertyDefinition.WithPropertyName<PropertyInfo<any, any>>;
export type ReactiveProperties = ReactiveProperty[];
/**
* @public
*/
export type PropertyDefinition<B> = {
/**
* ### Private Remarks
*
* This property only exists as a declaration and is only used for easier type inference.
*
* @internal scope: workspace
*/
Value: PropertyInfo.ToValue<B>;
AttributeName: PropertyInfo.ToAttributeName<B>;
/**
* A function to compare two property values to determine if they are equal.
*
* Defaults to a strict equality check (`===`) if not provided.
*/
comparator?: (a?: PropertyInfo.ToValue<B>, b?: PropertyInfo.ToValue<B>) => boolean;
attributeName?: PropertyInfo.ToAttributeName<B>;
toAttributeValue?: ToAttributeValue<PropertyInfo.ToValue<B>>;
toPropertyValue?: ToPropertyValue<PropertyInfo.ToValue<B>>;
};
export declare namespace PropertyDefinition {
type ToValue<T> = T extends PropertyDefinition<infer B> ? PropertyInfo.ToValue<B> : unknown;
type ToAttributeName<T> = T extends PropertyDefinition<infer B> ? PropertyInfo.ToAttributeName<B> : never;
namespace ToAttributeName {
type Valid<T> = T extends PropertyDefinition<infer B> ? PropertyInfo.ToAttributeName.Valid<B> : never;
}
type ToAttributeValue<T> = T extends PropertyDefinition<infer B> ? PropertyInfo.ToAttributeValue<B> : never;
type WithPropertyName<B, K extends PropertyName = PropertyName> = PropertyDefinition<B> & {
propertyName: K;
};
type FromValue<T, A extends AttributeName = false> = PropertyDefinition<PropertyInfo<T, A>>;
}
/**
* @public
*/
export type PropertyInfo<T, A extends AttributeName> = {
Value: T;
AttributeName: A;
};
export declare namespace PropertyInfo {
type ToValue<T> = T extends PropertyInfo<infer V, any> ? V : never;
type ToAttributeName<T> = T extends PropertyInfo<any, infer A> ? A : never;
namespace ToAttributeName {
type Valid<T> = T extends PropertyInfo<any, infer A> ? A extends string ? A : never : never;
}
type ToAttributeValue<T> = T extends PropertyInfo<infer V, any> ? V extends boolean ? boolean : string | null : never;
}
export type PropertiesDefinition<PropInfoDict> = {
[K in keyof PropInfoDict]: PropInfoDict[K] extends PropertyInfo<any, any> ? PropertyDefinition<PropInfoDict[K]> : PropInfoDict[K] extends PropertyDefinition<any> ? PropInfoDict[K] : never;
};
export declare namespace PropertiesDefinition {
type ToAttributes<T> = T extends PropertiesDefinition<infer B> ? {
[K in keyof B as PropertyInfo.ToAttributeName.Valid<B[K]>]: PropertyInfo.ToAttributeValue<B[K]> | undefined;
} : T extends Record<string, PropertyDefinition<any>> ? {
[K in keyof T as PropertyDefinition.ToAttributeName.Valid<T[K]>]: T[K] extends PropertyDefinition<infer I> ? PropertyInfo.ToAttributeValue<I> | undefined : never;
} : never;
type ToProps<T> = T extends PropertiesDefinition<infer B> ? {
[K in keyof B]: B[K] extends PropertyInfo<infer V, any> ? V | undefined : never;
} : T extends Record<string, PropertyDefinition<any>> ? {
[K in keyof T]: T[K] extends PropertyDefinition<infer I> ? PropertyInfo.ToValue<I> | undefined : never;
} : never;
/**
* Infers a `PropertyInfoDictionary` from a given set of props.
*
* This type is primarily used to verify that a `PropertiesDefinition` is correct.
* It should not be used to define a `PropertiesDefinition`, as it does not infer attribute names.
*
* @remarks
*
* Useful for resolving `TS7056` errors, where TypeScript encounters:
*
* > TS7056: The inferred type of this node exceeds the maximum length the
* compiler will serialize. An explicit type annotation is needed.
*
* To resolve:
* 1. Define a type for the expected props.
* 2. Have the class implement the expected props type.
* 3. Use `satisfies` to check if the `PropertiesDefinition` matches the expected props.
*
* @example
* ```ts
* type MyProps = {
* foo?: string;
* };
*
* class MyElement extends KnytElement implements MyProps {
* static properties = {
* foo: define.property().string().attribute("foo"),
* } satisfies PropertiesDefinition.FromProps<MyProps>;
*
* declare foo?: string;
* }
* ```
*/
type FromProps<T, A extends AttributeName = AttributeName> = {
[K in keyof T]: PropertyInfo<T[K], A>;
};
}
/**
* @internal scope: workspace
*/
export type HTMLElementConstructor<E extends HTMLElement = HTMLElement> = new () => E;
/**
* A definition for a custom element.
*
* @public
*/
export type ElementDefinition<T extends KnytElement.Constructor.Unknown, U extends string, P extends AnyProps = InstanceType<T>, A extends AttributeDictionary = KnytElement.ToAttributes<T>> = ElementDefinition.Fn<P> & ElementDefinition.Static<T, U, A>;
export declare namespace ElementDefinition {
/**
* @internal scope: workspace
*
* @returns A builder for declaring the HTML element using properties.
*/
type Fn<P extends AnyProps> = () => ElementBuilder.DOM<P>;
/**
* Static properties of an element definition for a `KnytElement`.
*
* @internal scope: workspace
*/
type Static<TConstructor extends KnytElement.Constructor.Unknown, TTagName extends string, TAttributes extends AttributeDictionary = KnytElement.ToAttributes<TConstructor>> = BaseStatic<TConstructor, TTagName, TAttributes>;
/**
* Static properties of an element definition.
*
* @internal scope: module
*/
type BaseStatic<TConstructor extends HTMLElementConstructor, TTagName extends string, TAttributes extends AttributeDictionary = AttributeDictionary> = {
/**
* @returns A builder for declaring the HTML element using attributes.
*/
readonly html: () => ElementBuilder.HTML<TAttributes>;
/**
* The constructor of the custom element.
*/
readonly Element: TConstructor;
/**
* The tag name of the custom element.
*/
readonly tagName: TTagName;
/**
* @internal scope: workspace
*/
readonly __isKnytElementDefinition: true;
};
/**
* @internal scope: workspace
*/
type ToStatic<T> = T extends ElementDefinition<infer T, infer U, any, infer A> ? Static<T, U, A> : never;
/**
* An unknown element definition.
*
* @internal scope: workspace
*/
type Unknown = ElementDefinition<KnytElement.Constructor.Unknown, string, AnyProps, AttributeDictionary>;
type FromPropertiesDefinition<TN extends string, PD extends PropertiesDefinition<any>> = ElementDefinition<KnytElement.Constructor.FromPropertiesDefinition<PD>, TN>;
type ToConstructor<T extends ElementDefinition<any, any, any, any>> = T extends ElementDefinition<infer TConstructor, any, any, any> ? TConstructor : never;
/**
* A definition for an element that is not a `KnytElement`.
*
* @public
*/
type Arbitrary<T extends HTMLElementConstructor, U extends string, P extends AnyProps = InstanceType<T>, A extends AttributeDictionary = AttributeDictionary> = ElementDefinition.Fn<P> & ElementDefinition.BaseStatic<T, U, A>;
namespace Arbitrary {
/** @internal scope: workspace */
type ToProps<T extends Arbitrary<any, any, any, any>> = T extends Arbitrary<any, any, infer P, any> ? P : never;
}
}
/**
* Infers reactive props from either a:
*
* - `PropertiesDefinition`
* - `ElementDefinition`
* - `ElementDefinition.Arbitrary`
* - `KnytElement` constructor
* - `View`
*
* @remarks
*
* To clarify, this type only infers reactive properties.
* It does not infer all of the implied properties of a
* an `HTMLElement`.
*
* @public
*/
export type InferProps<T> = T extends View<infer P> ? P : T extends KnytElement.Constructor.Unknown ? InferProps.ElementConstructor<T> : T extends ElementDefinition<KnytElement.Constructor.Unknown, any, any, any> ? InferProps.ElementConstructor<ElementDefinition.ToConstructor<T>> : T extends ElementDefinition.Arbitrary<any, any, any, any> ? InferProps.ElementConstructor.Arbitrary<T> : T extends PropertiesDefinition<any> ? Partial<PropertiesDefinition.ToProps<T>> : never;
export declare namespace InferProps {
/** @internal scope: workspace */
type ElementConstructor<T extends KnytElement.Constructor.Unknown> = Partial<PropertiesDefinition.ToProps<KnytElement.Constructor.ToPropertiesDefinition<T>>>;
namespace ElementConstructor {
/** @internal scope: workspace */
type Arbitrary<T extends ElementDefinition.Arbitrary<any, any, any, any>> = Partial<ElementDefinition.Arbitrary.ToProps<T>>;
}
/** @internal scope: workspace */
type HTML<T> = InferElementProps.HTML<T>;
/** @internal scope: workspace */
type SVG<T> = InferElementProps.SVG<T>;
/** @internal scope: workspace */
type DOM<T> = InferElementProps.DOM<T>;
}
//# sourceMappingURL=types.d.ts.map