Skip to content

Instantly share code, notes, and snippets.

@naramdash
Created March 12, 2023 12:28
Show Gist options
  • Save naramdash/85034a746224604e1e1ef639d486a399 to your computer and use it in GitHub Desktop.
Save naramdash/85034a746224604e1e1ef639d486a399 to your computer and use it in GitHub Desktop.
DOM creation
// https://stackoverflow.com/questions/49579094/typescript-conditional-types-filter-out-readonly-properties-pick-only-requir
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <
T
>() => T extends Y ? 1 : 2
? A
: B;
type WritableKeys<T> = {
[P in keyof T]-?: IfEquals<
{ [Q in P]: T[P] },
{ -readonly [Q in P]: T[P] },
P
>;
}[keyof T];
type ReadonlyKeys<T> = {
[P in keyof T]-?: IfEquals<
{ [Q in P]: T[P] },
{ -readonly [Q in P]: T[P] },
never,
P
>;
}[keyof T];
type FunctionLike =
| Function
| (Function | null)
| (Function | undefined)
| (Function | null | undefined);
type FunctionKeys<T> = {
[P in keyof T]: T[P] extends FunctionLike ? P : never;
}[keyof T];
type Attributes<T> = Partial<Omit<T, ReadonlyKeys<T> | FunctionKeys<T>>>;
// keys?
type RegisterEvent<Element extends HTMLElement> = {
type: "abort";
listener: (this: Element, ev: HTMLElementEventMap["abort"]) => any;
options?: boolean | AddEventListenerOptions;
};
function hyper<
TagName extends keyof HTMLElementTagNameMap,
Element extends HTMLElementTagNameMap[TagName]
>(
tagName: TagName,
attributes: Attributes<Element>,
listenerRegisters: RegisterEvent<Element>[],
children: (string | Node)[]
) {
const element = document.createElement(tagName);
for (const attributeName in attributes)
element.setAttribute(
attributeName,
// @ts-ignore
attributes[attributeName]
);
for (const listenerRegister of listenerRegisters)
element.addEventListener(
listenerRegister.type,
// @ts-ignore
listenerRegister.listener,
listenerRegister.options
);
element.replaceChildren(...children);
return element;
}
export { hyper };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment