ts: Migrate `vdom.js` to TypeScript.

Added type annotations for variables, function params, and return
values. Created custom types that help with clean type annotations.

Notes on `attrs` field in custom `Option` type:
 `attrs` is an `Iterable` where each element is a pair of string, i.e
a string array with two elements. `attrs` is tranformed into a `Map`
at some point. `Map` constructor takes in `Iterable` object so has
no problem unifying with `attrs`. However, at some point `attrs` is
transfomed using `.map(...)` which is an array method, and `Iterable`
does not support `.map(...)`. So at this point, I cast `attrs` into
array before using `.map(...)` by this syntax:
`[..attrs].map(...)`
This commit is contained in:
Junyao Chen 2023-07-17 04:01:20 -04:00 committed by Tim Abbott
parent 5d819ea254
commit 30c535fc63
1 changed files with 36 additions and 10 deletions

View File

@ -2,7 +2,26 @@ import _ from "lodash";
import * as blueslip from "./blueslip";
export function eq_array(a, b, eq) {
type Equal<T> = (...args: T[]) => boolean;
type Node = {
key: number;
render: () => string;
name: string;
eq: Equal<number | Node>;
};
type Options = {
attrs: [string, string][];
keyed_nodes: Node[];
};
type Tag = {
tag_name: string;
opts: Options;
};
export function eq_array<T>(a: T[] | undefined, b: T[] | undefined, eq: Equal<T>): boolean {
if (a === b) {
// either both are undefined, or they
// are referentially equal
@ -20,14 +39,14 @@ export function eq_array(a, b, eq) {
return a.every((item, i) => eq(item, b[i]));
}
export function ul(opts) {
export function ul(opts: Options): Tag {
return {
tag_name: "ul",
opts,
};
}
export function render_tag(tag) {
export function render_tag(tag: Tag): string | undefined {
/*
This renders a tag into a string. It will
automatically escape attributes, but it's your
@ -41,9 +60,7 @@ export function render_tag(tag) {
*/
const opts = tag.opts;
const tag_name = tag.tag_name;
const attr_str = opts.attrs
.map((attr) => " " + attr[0] + '="' + _.escape(attr[1]) + '"')
.join("");
const attr_str = opts.attrs.map((attr) => ` ${attr[0]}="${_.escape(attr[1])}"`).join("");
const start_tag = "<" + tag_name + attr_str + ">";
const end_tag = "</" + tag_name + ">";
@ -53,11 +70,15 @@ export function render_tag(tag) {
return undefined;
}
const innards = opts.keyed_nodes.map((node) => node.render()).join("\n");
const innards = opts.keyed_nodes.map((node: Node) => node.render()).join("\n");
return start_tag + "\n" + innards + "\n" + end_tag;
}
export function update_attrs($elem, new_attrs, old_attrs) {
export function update_attrs(
$elem: JQuery,
new_attrs: Iterable<[string, string]>,
old_attrs: Iterable<[string, string]>,
): void {
const new_dict = new Map(new_attrs);
const old_dict = new Map(old_attrs);
@ -74,7 +95,12 @@ export function update_attrs($elem, new_attrs, old_attrs) {
}
}
export function update(replace_content, find, new_dom, old_dom) {
export function update(
replace_content: (html: string | undefined) => void,
find: () => JQuery,
new_dom: Tag,
old_dom: Tag | undefined,
): void {
/*
The update method allows you to continually
update a "virtual" representation of your DOM,
@ -126,7 +152,7 @@ export function update(replace_content, find, new_dom, old_dom) {
For examples of creating vdom objects, look at
`pm_list_dom.js`.
*/
function do_full_update() {
function do_full_update(): void {
const rendered_dom = render_tag(new_dom);
replace_content(rendered_dom);
}