Notes on TypeScript
Top and Bottom Types
Unlike Anything or Any in most static typed languages,
any supports the same operations as a value in JavaScript
and minimal static type checking is performed.
TypeScript's unknown is more similar to Any (top type) in other languages.
TypeScript's bottom type is never, which is called Nothing in some languages.
const verifyCasesAreExhaustive = (kind: "x" | "y" | "z") => {
switch (kind) {
case "x":
return 1
case "y":
return 2
case "z":
return 3
default:
const nothing: never = kind
throw(nothing)
}
}
Function Overloads
Considering the following definition:
function plus(x: number, y: 0): boolean;
function plus(x: number, y: number): number;
function plus(x: string, y: string): string;
// function plus(x: number, y: string): boolean;
// function plus(x: string, y: number): boolean;
function plus(x: number | string, y: number | string): number | string | boolean {
if (typeof x == "number" && typeof y == "number") {
if (y === 0) {
return false
} else {
return x + y;
}
} else if (typeof x == "string" && typeof y == "string") {
return `${x}${y}`; // equivalent to `x + y`.
} else {
return false;
}
}
In early versions of TypeScript (e.g. [v3.5.1]),
the following code gives misleading error information:
```typescript
plus("1", 0)
// error: Argument of type '0' is not assignable to parameter of type 'string'.
Readers may wonder why TypeScript does not match ("1", 0) against plus(x: number | string, y: number | string).
However, recent versions of TypeScript (e.g. v5.3.2) provides clearer message:
plus("1", 0)
// The call would have succeeded against this implementation,
// but implementation signatures of overloads are not externally visible
Type Equality
With TypeScript's conditional types, testing type equality is intuitive:
type EqEq<T, S> = [T] extends [S] ? ([S] extends [T] ? true : false) : false
type FunctionOverloadsEquality = EqEq<
{ (x: 0, y: null): void; (x: number, y: null): void },
{ (x: number, y: null): void; (x: 0, y: null): void }> // true
type F = (x: 0, y: null) => void
type G = (x: number, y: string) => void
type FunctionIntersectionEquality = EqEq<F & G, G & F> // true
The Crazy satisfies Operator
Below is the example given in What's New in TypeScript 4.9:
type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];
const palette = {
red: [255, 0, 0],
green: "#00ff00",
bleu: [0, 0, 255]
// ~~~~ The typo is now caught!
} satisfies Record<Colors, string | RGB>;
// Both of these methods are still accessible!
const redComponent = palette.red.at(0);
const greenNormalized = palette.green.toUpperCase();
IMHO, this shows the craziness of TypeScript.
Why green is encoded as hex string but red and blue are encoded as RGB arrays?
What if one day someone refactors the code and change green to an RGB array?
And in real world application, I would rather make all colors encoded as hex strings or RGB arrays,
but not a mix of them.
Then the getComponent or displayHex will be implemented assuming input is either a hex string or an RGB array.