better-typescript

Various TypeScript type definitions to make working with TypeScript more convenient


License
WTFPL
Install
npm install better-typescript@0.0.3

Documentation

Better TypeScript

This repository contains various TypeScript type definitions to make working with TypeScript more convenient.

This project goes along with TypeScript types for new JavaScript, which contains TypeScript type definitions for new JavaScript stuff that isn't in TypeScript's standard type definitions. Better TypeScript depends on TypeScript types for new JavaScript, so it is automatically included if you use Better TypeScript.


NPM: better-typescript

GitHub: BenjaminAster/Better-TypeScript


Install using npm:

npm i -D better-typescript@latest

Reference the type definitions directly in your TypeScript/JavaScript files...

/// <reference types="better-typescript" />

...or include them in your tsconfig.json or jsconfig.json:

{
	"compilerOptions": {
		"types": ["better-typescript"],
	},
}

Inside of a web worker, use better-typescript/worker as the path:

/// <reference no-default-lib="true" />
/// <reference types="better-typescript/worker" />

Stuff in this repository

querySelector element parser

A querySelector parser that parses the CSS selector and automatically returns the interface for the respective element:

document.querySelector("a#foo.bar") // HTMLAnchorElement
element.querySelector("form.info > input[type=radio][name=test]:nth-of-type(even)") // HTMLInputElement
document.querySelector(".math-output mrow ~ munderover[displaystyle=false]") // MathMLElement
document.querySelector("svg#logo > filter:first-of-child feTurbulence:not([type=fractalNoise])") // SVGFETurbulenceElement

Just to be clear: This parser is not written in TypeScript, it's written solely in TypeScript type definitions (files ending in .d.ts). This works similar to HypeScript.

Service workers

Working with service workers with type checking enabled is an awful experience by default as in TypeScript, there is no ServiceWorker lib, only a WebWorker one. Stuff like self.registration, self.clients or the fetch event aren't available by default because from TypeScript's perspecitve, self alywas has the type WorkerGlobalScope in workers, not ServiceWorkerGlobalScope. The way you could previously get around this is by declaring a variable const _self = self as unknown as ServiceWorkerGlobalScope; and then working with this _self instead of self as the global object. This is very ugly and hacky, so Better TypeScript simply provides all service worker related stuff out of the box to any web worker with the better-typescript/worker types.

/// <reference no-default-lib="true" />
/// <reference types="better-typescript/worker" />

self.addEventListener("fetch", (event) => {
	// `event` is of type `FetchEvent`
})

Accept not just strings

Many JavaScript functions also accept numbers which then get automatically converted into a string. TypeScript often just accepts strings, so Better TypeScript adds the ability to call a function with numbers instead of strings.

window.addEventListener("pointermove", (event) => {
	document.documentElement.style.setProperty("--mouse-x", event.clientX); // would be an error without Better TypeScript
	document.documentElement.style.setProperty("--mouse-y", event.clientY);
)};
const searchParams = new URLSearchParams(location.search);
searchParams.set("count", ++count); // would be an error without Better TypeScript
history.replaceState(null, "", "?" + searchParams.toString());

cloneNode

When calling .cloneNode() on any element or fragment, TypeScript just returns the Node type by default which is not very useful. With Better TypeScript, .cloneNode() returns the type of the element that it is called on.

const anchorTemplate = document.createElement("a");
anchorTemplate.href = "https://example.com";
anchorTemplate.textContent = "example";
const anchorClone = anchorTemplate.cloneNode(true); // `anchorClone` now has the type `HTMLAnchorElement` instead of just `Node`
const fragment = document.querySelector("template#foo-template").content;

for (let i = 0; i < 10; i++) {
	const clone = fragment.cloneNode(true); // `clone` now has the type `DocumentFragment` instead of just `Node`
	clone.querySelector("a.destination").href = "https://example.com";
	document.body.append(clone);
}

MathML

This adds support for MathML element names as well as the MathML namespace for the document.createElementNS() and document.getElementsByTagNameNS functions.

document.querySelector("math"); // now returns type `MathMLElement` instead of `Element`
document.querySelector("mfrac"); // now returns type `MathMLElement` instead of `Element`
document.createElementNS("http://www.w3.org/1998/Math/MathML", "msub"); // now returns type `MathMLElement` instead of `Element`

OffscreenCanvas

When getting a specific canvas context (e.g. "2d", "webgl2", ...) from a normal canvas element, TypeScript automatically detects the context type and returns the respective type in the .getContext() method.

const canvas = document.querySelector("canvas");
canvas.getContext("2d", { alpha: false }); // returns type `CanvasRenderingContext2D` & the options parameter is of type `CanvasRenderingContext2DSettings`

However, when using an OffscreenCanvas, TypeScript does not provide you with these type definitions and always just returns the OffscreenCanvasRenderingContext type which is not very useful. Better TypeScript adds the canvas context types and their option interfaces.

const canvas = new OffscreenCanvas(width, height);
canvas.getContext("2d", { alpha: false }); // now correctly returns type `CanvasRenderingContext2D` & the options parameter is of type `OffscreenCanvasRenderingContext2DSettings`

Non-standard stuff

This includes various non-standard features that are not part of any specification, e.g. Chromium's chrome, Brave's navigator.brave.isBrave() or Firefox' CSSMozDocumentRule. This can also be used for spoofing-resistant browser detection.

const isBrave = await navigator.brave?.isBrave?.();
// Chromium & WebKit's non-standard scrollIntoViewIfNeeded() function
if (Element.prototype.scrollIntoViewIfNeeded) element.scrollIntoViewIfNeeded(true);
else element.scrollIntoView({ block: "center" });