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 = {
255, 0, 0],
red: ["#00ff00",
green: 0, 0, 255]
bleu: [// ~~~~ 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.