This guide covers just enough TypeScript to read and work with the ArborView source code. If you know Python well, TypeScript will feel familiar — it's a statically-typed language with similar syntax and many analogous constructs.
TypeScript is JavaScript with a type system bolted on — think of it as Python with mandatory type hints, where the type checker runs at compile time rather than as an optional linter. Key differences from Python:
- Types are required (or inferred), not optional annotations
- Code compiles to plain JavaScript before running in the browser
- Semicolons terminate statements (optional but conventional)
- There's no pip/conda; packages come from npm
Python variables are just assignment. TypeScript distinguishes between reassignable (let) and fixed (const) bindings:
# Python
name = "ArborView"
count = 0
count += 1// TypeScript
const name = "ArborView"; // like a final binding — can't reassign
let count = 0;
count += 1;Prefer const by default; use let only when you need to reassign. You'll rarely see var in modern TypeScript — treat it like Python 2's print statement.
Python type hints are optional. TypeScript types are enforced at compile time:
# Python — hints are optional, ignored at runtime
def greet(name: str) -> str:
return f"Hello, {name}"// TypeScript — types are required, checked at compile time
function greet(name: string): string {
return `Hello, ${name}`;
}TypeScript often infers types from context, so you don't always have to write them:
const title = "ArborView"; // TypeScript infers: string
const n = 42; // TypeScript infers: number| Python | TypeScript |
|---|---|
str |
string |
int / float |
number (no distinction) |
bool |
boolean |
None |
null or undefined |
list |
array [] |
dict |
object {} |
Python uses TypedDict, dataclasses, or Pydantic models to describe the shape of a dict/object. TypeScript uses type aliases or interface:
# Python
from typing import TypedDict
class TreeNode(TypedDict):
node_id: int
depth: int
is_leaf: bool// TypeScript
type TreeNode = {
node_id: number;
depth: number;
is_leaf: boolean;
};These are equivalent in intent. The TypeScript compiler checks that every TreeNode you create has these fields at the right types.
Python's Optional[X] (or X | None) has a direct TypeScript equivalent:
# Python
from typing import Optional
class TreeNode(TypedDict):
predicted_class: Optional[str] # may be absent for regression trees// TypeScript
type TreeNode = {
predicted_class?: string; // the ? means: present or absent
};A ? on a field means it may be undefined (not present). Fields that are explicitly null in the JSON are typed as string | null instead.
Python's Union[A, B] (or A | B in Python 3.10+) maps directly:
# Python
from typing import Union
Split = Union[NumericSplit, CategoricalSplit]
ResponseType = Literal["classification", "regression"]// TypeScript
type Split = NumericSplit | CategoricalSplit;
type ResponseType = "classification" | "regression";String literal unions like ResponseType above are TypeScript's equivalent of Literal — only those exact strings are valid.
TypeScript arrays are like Python lists, with 0-based indexing. The type string[] means "array of strings":
# Python
levels = ["setosa", "versicolor", "virginica"]
levels[0] # "setosa"// TypeScript
const levels: string[] = ["setosa", "versicolor", "virginica"];
levels[0] // "setosa"TypeScript objects work like Python dicts with string keys, but accessed with . instead of []:
# Python
node = {"node_id": 1, "depth": 0, "n": 100}
node["depth"] # 0// TypeScript
const node = { node_id: 1, depth: 0, n: 100 };
node.depth // 0 (dot access is conventional for known-shape objects)For a dict with arbitrary string keys (like Python's dict[str, float]), TypeScript uses Record<string, number>:
const importance: Record<string, number> = {
wt: 847.7,
cyl: 785.6,
};Python lambdas are single-expression only. TypeScript arrow functions are full functions and are used everywhere:
# Python
square = lambda x: x ** 2
nodes_n = list(map(lambda n: n["n"], nodes))// TypeScript
const square = (x: number) => x ** 2;
const nodesN = nodes.map((n) => n.n);Multi-line arrow functions use {} and an explicit return:
const describe = (node: TreeNode) => {
const label = node.is_leaf ? "leaf" : "internal";
return `${label} node at depth ${node.depth}`;
};Python f-strings map directly to TypeScript template literals (backtick strings):
# Python
f"Node {node_id} at depth {depth}"// TypeScript
`Node ${node_id} at depth ${depth}`TypeScript has two "nothing" values. This trips up Python developers:
null— explicitly set to nothing (like Python'sNone)undefined— variable was declared but never assigned, or an optional field is absent
In ArborView's JSON data, missing values are null. Use strict equality to check:
if (node.predicted_class !== null) {
// safe to use node.predicted_class here
}Avoid == (loose equality) — always use ===. In TypeScript, null == undefined is true but null === undefined is false.
Python's from module import X maps directly:
# Python
from types import TreeNode, Arbor// TypeScript
import { TreeNode, Arbor } from "./types";The ./ prefix means the path is relative to the current file. Packages from npm are imported without a path prefix (e.g., import * as d3 from "d3").
Python's isinstance() checks narrow types. TypeScript does the same:
# Python
if isinstance(split, NumericSplit):
print(split.threshold) # type checker knows this is NumericSplit here// TypeScript
if (split.type === "numeric") {
console.log(split.threshold); // TypeScript knows it's NumericSplit here
}Here's the top-level Arbor type from src/types.ts with Python-lens annotations:
export type Arbor = {
schema: string; // str — version tag
title: string; // str — display label
method: string; // "class" or "anova"
response: {
type: ResponseType; // Literal["classification", "regression"]
levels: string[] | null; // list[str] | None
};
variables: {
predictors: string[]; // list[str]
importance: Record<string, number>; // dict[str, float]
};
cptable?: CpTableRow[]; // list[CpTableRow] | None (optional field)
call: string; // str — the R call that built the model
tree: TreeNode; // root node; children nested recursively
};export makes the type importable by other files — like __all__ in a Python module.
The ArborView TypeScript lives in src/:
src/types.ts— all type definitions (good first read)src/main.ts— loads data, wires up the sidebar UIsrc/tree.ts— D3 tree renderersrc/tooltip.ts— tooltip rendering
For a deeper dive, the TypeScript Handbook is thorough and well-written. If you're coming from Python specifically, the TypeScript for Python Programmers page in the handbook is also worth a skim.