TypeScript Notes
đź“– tl;dr: Dump of Notes for TypeScript
-
Your existing working JavaScript code is also TypeScript code.
-
TypeScript knows the JavaScript language and will generate/infer types for you in many cases.
let helloWorld = "Hello World"; -
You can explicitly describe this object’s shape using an interface declaration:
interface User { name: string; id: number; } -
You should prefer
interface. Usetypewhen you need specific features. -
Generics provide variables to type:
type StringArray = Array<string>; type NumberArray = Array<number>; type ObjectWithNameArray = Array<{ name: string }>; -
One of TypeScript’s core principles is that type checking focuses on the shape that
values have. This is sometimes calledduck typingorstructural typing. -
There is no difference between how classes and objects conform to shapes:
class VirtualPoint { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } } const newVPoint = new VirtualPoint(13, 56); logPoint(newVPoint); // logs "13, 56" -
TypeScript act a bit more strictly. In that case, you can use the
noEmitOnErrorcompiler option. -
That’s why TypeScript needs a compiler in the first place - it needs some way to strip out or transform any TypeScript-specific code so that you can run it.
-
Using any often defeats the purpose of using TypeScript in the first place. The more typed your program is, the more validation and tooling you’ll get.
-
Turning on the
noImplicitAnyflag will issue an error on any variables whose type is implicitly inferred as any. -
The type names
String,Number, andBoolean(starting with capital letters) are legal, but refer to some special built-in types that will very rarely appear in your code. Always usestring,number, orbooleanfor types. -
To specify the type of an array like
[1, 2, 3], you can use the syntaxnumber[]. -
Note that
[number]is a different thing; refer to the section onTuples. -
When you don’t specify a type, and TypeScript can’t infer it from context, the compiler will typically default to
any. Becauseanyisn’t type-checked. Use the compiler flagnoImplicitAnyto flag any implicitanyas an error. -
TypeScript doesn’t use types on the left style declarations like
int x = 0;Type annotations will always go after the thing being typed. -
const names = ["Alice", "Bob", "Eve"]; // Contextual typing for function - parameter s inferred to have type string names.forEach(function (s) { console.log(s.toUpperCase()); }); // Contextual typing also applies to arrow functions names.forEach((s) => { console.log(s.toUpperCase()); });Even though the parameter
sdidn’t have a type annotation, TypeScript used the types of theforEachfunction, along with theinferredtype of the array, to determine the typeswill have.This process is called
contextual typingbecause thecontextthat the function occurred within informs what type it should have. -
Object types can also specify that some or all of their properties are optional. To do this, add a ? after the property name:
function printName(obj: { first: string; last?: string }) { // ... } // Both OK printName({ first: "Bob" }); printName({ first: "Alice", last: "Alisson" }); -
A union type is a type formed from two or more other types, representing values that may be any one of those types.
-
TypeScript will only allow an operation if it is valid for every member of the union. For example, if you have the union
string | number, you can’t use methods that are only available on string:function printId(id: number | string) { console.log(id.toUpperCase()); // Property 'toUpperCase' does not exist on type 'string | number'. // Property 'toUpperCase' does not exist on type 'number'. } -
Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.
// Extending an interface interface Animal { name: string; } interface Bear extends Animal { honey: boolean; } const bear = getBear(); bear.name; bear.honey; // Extending a type via intersections type Animal = { name: string; } type Bear = Animal & { honey: boolean; } const bear = getBear(); bear.name; bear.honey; -
You can use a type assertion to specify a more specific type:
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement; -
You can also use the angle-bracket syntax (except if the code is in a .tsx file), which is equivalent:
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas"); -
By themselves, literal types aren’t very valuable:
let x: "hello" = "hello"; // OK x = "hello"; // ... x = "howdy"; // Type '"howdy"' is not assignable to type '"hello"'.It’s not much use to have a variable that can only have one value! But by combining literals into unions, you can express a much more useful concept - for example, functions that only accept a certain set of known values:
function printText(s: string, alignment: "left" | "right" | "center") { // ... } printText("Hello, world", "left"); printText("G'day, mate", "centre"); // Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'. -
TypeScript also has a special syntax for removing null and undefined from a type without doing any explicit checking. Writing
!after any expression is effectively a type assertion that the value isn’t null or undefined:function liveDangerously(x?: number | null) { // No error console.log(x!.toFixed()); } - Arrays are object types in JavaScript.
-
But it turns out that in JavaScript,
typeof nullis actually"object"! -
For methods, note that the parameter name is required. The function type
(string) => voidmeans a function with a parameter namedstringof typeany!