[[TypeScript]]'s type system is famously unsound (meaning that, there exists code that passes type checks but would fail at runtime) but in practice, it works surprisingly well. You rarely encounter type-related runtime errors with a sufficiently well-structured typescript codebase. This note highlights the examples where this may occur, and how to avoid them. # The Basics These are more in the realm of bugs and sloppy code: - Misusing `any`. This is by design. With strict `tsconfig` configurations and linter rules, you don't encounter the code is forced to. - Misusing `as` cast. - Incorrect type guards and type assertions. - Incorrect type definitions against a non-typescript library. # Mutation There are many examples of this, but once mutation is involved, one cannot guarantee type safety. ```typescript type aNumber = { a: number }; type aStringNumber = { a: number | string }; const obj: aNumber = { a: 1 }; function mutate(input: aStringNumber) { input.a = "1"; } mutate(obj); const numVariable: number = obj.a; // in reality, string. ``` # [[Covariance and Contravariance]] `Child[]` can be casted to `Parent[]` without any casting. As arrays are mutable, this can introduce unsoundness. ```typescript type Animal = "cat" | "dog" | "frog"; type Mammal = "cat" | "dog"; const mammals: Mammal[] = ["cat", "dog"]; const animals: Animal[] = mammals; animals.push("frog"); const shouldBeMammal: Mammal = mammals[2]; ```