TypeScript for JavaScript Programmers
+TypeScript for JavaScript Programmers
For example, JavaScript provides language primitives like string and number, but it doesn't check that you've consistently assigned these. TypeScript does.
This means that your existing working JavaScript code is also TypeScript code. The main benefit of TypeScript is that it can highlight unexpected behavior in your code, lowering the chance of bugs.
This tutorial provides a brief overview of TypeScript, focusing on its type system.
-Types by Inference
+Types by Inference
TypeScript knows the JavaScript language and will generate types for you in many cases. For example in creating a variable and assigning it to a particular value, TypeScript will use the value as its type.
-let helloWorld = "Hello World";
-// ^?
-
+let helloWorld = "Hello World";
+// ^?
+
By understanding how JavaScript works, TypeScript can build a type-system that accepts JavaScript code but has types. This offers a type-system without needing to add extra characters to make types explicit in your code. That's how TypeScript knows that helloWorld is a string in the above example.
You may have written JavaScript in Visual Studio Code, and had editor auto-completion. Visual Studio Code uses TypeScript under the hood to make it easier to work with JavaScript.
-Defining Types
+Defining Types
You can use a wide variety of design patterns in JavaScript. However, some design patterns make it difficult for types to be inferred automatically (for example, patterns that use dynamic programming). To cover these cases, TypeScript supports an extension of the JavaScript language, which offers places for you to tell TypeScript what the types should be.
For example, to create an object with an inferred type which includes name: string and id: number, you can write:
const user = {
- name: "Hayes", // [!code --]
- id: 0,// [!code ++]
-};
-
+const user = {
+ name: "Hayes", // [!code --]
+ id: 0,// [!code ++]
+};
+
You can explicitly describe this object's shape using an interface declaration:
interface User {
- name: string;
- id: number;
-}
-
+interface User {
+ name: string;
+ id: number;
+}
+
You can then declare that a JavaScript object conforms to the shape of your new interface by using syntax like : TypeName after a variable declaration:
interface User {
- name: string;
- id: number;
-}
-// ---cut---
-const user: User = {
- name: "Hayes",
- id: 0,
-};
-
+interface User {
+ name: string;
+ id: number;
+}
+// ---cut---
+const user: User = {
+ name: "Hayes",
+ id: 0,
+};
+
If you provide an object that doesn't match the interface you have provided, TypeScript will warn you:
-// @errors: 2322
-interface User {
- name: string;
- id: number;
-}
-
-const user: User = {
- username: "Hayes", // [!code word:username]
- id: 0,
-};
-
+// @errors: 2322
+interface User {
+ name: string;
+ id: number;
+}
+
+const user: User = {
+ username: "Hayes",
+ id: 0,
+};
+
Since JavaScript supports classes and object-oriented programming, so does TypeScript. You can use an interface declaration with classes:
-interface User {
- name: string;
- id: number;
-}
-
-class UserAccount {
- name: string;
- id: number;
-
- constructor(name: string, id: number) {
- this.name = name;
- this.id = id;
- }
-}
-
-const user: User = new UserAccount("Murphy", 1);
-
+interface User {
+ name: string;
+ id: number;
+}
+
+class UserAccount {
+ name: string;
+ id: number;
+
+ constructor(name: string, id: number) {
+ this.name = name;
+ this.id = id;
+ }
+}
+
+const user: User = new UserAccount("Murphy", 1);
+
You can use interfaces to annotate parameters and return values to functions:
-// @noErrors
-interface User {
- name: string;
- id: number;
-}
-// ---cut---
-function deleteUser(user: User) {
- // ...
-}
-
-function getAdminUser(): User {
- //...
-}
-
+// @noErrors
+interface User {
+ name: string;
+ id: number;
+}
+// ---cut---
+function deleteUser(user: User) {
+ // ...
+}
+
+function getAdminUser(): User {
+ //...
+}
+
There is already a small set of primitive types available in JavaScript: boolean, bigint, null, number, string, symbol, and undefined, which you can use in an interface. TypeScript extends this list with a few more, such as any (allow anything), unknown (ensure someone using this type declares what the type is), never (it's not possible that this type could happen), and void (a function which returns undefined or has no return value).
You'll see that there are two syntaxes for building types: Interfaces and Types. You should prefer interface. Use type when you need specific features.
Composing Types
+Composing Types
With TypeScript, you can create complex types by combining simple ones. There are two popular ways to do so: with unions, and with generics.
-Unions
+Unions
With a union, you can declare that a type could be one of many types. For example, you can describe a boolean type as being either true or false:
type MyBool = true | false;
-
+type MyBool = true | false;
+
Note: If you hover over MyBool above, you'll see that it is classed as boolean. That's a property of the Structural Type System. More on this below.
A popular use-case for union types is to describe the set of string or number literals that a value is allowed to be:
type WindowStates = "open" | "closed" | "minimized";
-type LockStates = "locked" | "unlocked";
-type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
-
+type WindowStates = "open" | "closed" | "minimized";
+type LockStates = "locked" | "unlocked";
+type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
+
Unions provide a way to handle different types too. For example, you may have a function that takes an array or a string:
function getLength(obj: string | string[]) {
- return obj.length;
-}
-
+function getLength(obj: string | string[]) {
+ return obj.length;
+}
+
To learn the type of a variable, use typeof:
For example, you can make a function return different values depending on whether it is passed a string or an array:
-function wrapInArray(obj: string | string[]) {
- if (typeof obj === "string") {
- return [obj];
-// ^?
- }
- return obj;
-}
-
-Generics
+function wrapInArray(obj: string | string[]) {
+ if (typeof obj === "string") {
+ return [obj];
+// ^?
+ }
+ return obj;
+}
+
+Generics
Generics provide variables to types. A common example is an array. An array without generics could contain anything. An array with generics can describe the values that the array contains.
-type StringArray = Array<string>;
-type NumberArray = Array<number>;
-type ObjectWithNameArray = Array<{ name: string }>;
-
+type StringArray = Array<string>;
+type NumberArray = Array<number>;
+type ObjectWithNameArray = Array<{ name: string }>;
+
You can declare your own types that use generics:
-// @errors: 2345
-interface Backpack<Type> {
- add: (obj: Type) => void;
- get: () => Type;
-}
-
-// This line is a shortcut to tell TypeScript there is a
-// constant called `backpack`, and to not worry about where it came from.
-declare const backpack: Backpack<string>;
-
-// object is a string, because we declared it above as the variable part of Backpack.
-const object = backpack.get();
-
-// Since the backpack variable is a string, you can't pass a number to the add function.
-backpack.add(23);
-
-Structural Type System
+// @errors: 2345
+interface Backpack<Type> {
+ add: (obj: Type) => void;
+ get: () => Type;
+}
+
+// This line is a shortcut to tell TypeScript there is a
+// constant called `backpack`, and to not worry about where it came from.
+declare const backpack: Backpack<string>;
+
+// object is a string, because we declared it above as the variable part of Backpack.
+const object = backpack.get();
+
+// Since the backpack variable is a string, you can't pass a number to the add function.
+backpack.add(23);
+
+Structural Type System
One of TypeScript's core principles is that type checking focuses on the shape that values have. This is sometimes called "duck typing" or "structural typing".
In a structural type system, if two objects have the same shape, they are considered to be of the same type.
-interface Point {
- x: number;
- y: number;
-}
-
-function logPoint(p: Point) {
- console.log(`${p.x}, ${p.y}`);
-}
-
-// logs "12, 26"
-const point = { x: 12, y: 26 };
-logPoint(point);
-
+interface Point {
+ x: number;
+ y: number;
+}
+
+function logPoint(p: Point) {
+ console.log(`${p.x}, ${p.y}`);
+}
+
+// logs "12, 26"
+const point = { x: 12, y: 26 };
+logPoint(point);
+
The point variable is never declared to be a Point type. However, TypeScript compares the shape of point to the shape of Point in the type-check. They have the same shape, so the code passes.
The shape-matching only requires a subset of the object's fields to match.
-// @errors: 2345
-interface Point {
- x: number;
- y: number;
-}
-
-function logPoint(p: Point) {
- console.log(`${p.x}, ${p.y}`);
-}
-// ---cut---
-const point3 = { x: 12, y: 26, z: 89 };
-logPoint(point3); // logs "12, 26"
-
-const rect = { x: 33, y: 3, width: 30, height: 80 };
-logPoint(rect); // logs "33, 3"
-
-const color = { hex: "#187ABF" };
-logPoint(color);
-
+// @errors: 2345
+interface Point {
+ x: number;
+ y: number;
+}
+
+function logPoint(p: Point) {
+ console.log(`${p.x}, ${p.y}`);
+}
+// ---cut---
+const point3 = { x: 12, y: 26, z: 89 };
+logPoint(point3); // logs "12, 26"
+
+const rect = { x: 33, y: 3, width: 30, height: 80 };
+logPoint(rect); // logs "33, 3"
+
+const color = { hex: "#187ABF" };
+logPoint(color);
+
There is no difference between how classes and objects conform to shapes:
-// @errors: 2345
-interface Point {
- x: number;
- y: number;
-}
-
-function logPoint(p: Point) {
- console.log(`${p.x}, ${p.y}`);
-}
-// ---cut---
-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"
-
+// @errors: 2345
+interface Point {
+ x: number;
+ y: number;
+}
+
+function logPoint(p: Point) {
+ console.log(`${p.x}, ${p.y}`);
+}
+// ---cut---
+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"
+
If the object or class has all the required properties, TypeScript will say they match, regardless of the implementation details.
-Next Steps
+Next Steps
This was a brief overview of the syntax and tools used in everyday TypeScript. From here, you can:
- Read the full Handbook from start to finish @@ -262,7 +262,7 @@
Next Steps
TypeScript for the New Programmer
+TypeScript for the New Programmer
Congratulations on choosing TypeScript as one of your first languages — you're already making good decisions!
You've probably already heard that TypeScript is a "flavor" or "variant" of JavaScript. The relationship between TypeScript (TS) and JavaScript (JS) is rather unique among modern programming languages, so learning more about this relationship will help you understand how TypeScript adds to JavaScript.
-What is JavaScript? A Brief History
+What is JavaScript? A Brief History
JavaScript (also known as ECMAScript) started its life as a simple scripting language for browsers. At the time it was invented, it was expected to be used for short snippets of code embedded in a web page — writing more than a few dozen lines of code would have been somewhat unusual. Due to this, early web browsers executed such code pretty slowly. @@ -41,64 +41,67 @@
What is JavaScript? A Brief History
-
JavaScript's equality operator (
-==) coerces its operands, leading to unexpected behavior:
+if ("" == 0) { - // It is! But why?? -} -if (1 < x < 3) { - // True for *any* value of x! -} -if ("" == 0) { + // It is! But why?? +} +if (1 < x < 3) { + // True for *any* value of x! +} + -
JavaScript also allows accessing properties which aren't present:
-
+const obj = { width: 10, height: 15 }; -// Why is this NaN? Spelling is hard! -const area = obj.width * obj.heigth; -const obj = { width: 10, height: 15 }; +// Why is this NaN? Spelling is hard! +const area = obj.width * obj.heigth; +
Most programming languages would throw an error when these sorts of errors occur, some would do so during compilation — before any code is running. When writing small programs, such quirks are annoying but manageable; when writing applications with hundreds or thousands of lines of code, these constant surprises are a serious problem.
-TypeScript: A Static Type Checker
+TypeScript: A Static Type Checker
We said earlier that some languages wouldn't allow those buggy programs to run at all. Detecting errors in code without running it is referred to as static checking. Determining what's an error and what's not based on the kinds of values being operated on is known as static type checking.
TypeScript checks a program for errors before execution, and does so based on the kinds of values, making it a static type checker.
For example, the last example above has an error because of the type of obj.
Here's the error TypeScript found:
// @errors: 2551
-const obj = { width: 10, height: 15 };
-const area = obj.width * obj.heigth;
-
-A Typed Superset of JavaScript
+const obj = { width: 10, height: 15 };
+const area = obj.width * obj.heigth;
+Property 'heigth' does not exist on type '{ width: number; height: number; }'. Did you mean 'height'?
+
+
+A Typed Superset of JavaScript
How does TypeScript relate to JavaScript, though?
-Syntax
+Syntax
TypeScript is a language that is a superset of JavaScript: JS syntax is therefore legal TS.
Syntax refers to the way we write text to form a program.
For example, this code has a syntax error because it's missing a ):
// @errors: 1005
-let a = (4
-
+// @errors: 1005
+let a = (4
+')' expected
+
+
TypeScript doesn't consider any JavaScript code to be an error because of its syntax. This means you can take any working JavaScript code and put it in a TypeScript file without worrying about exactly how it is written.
-Types
+Types
However, TypeScript is a typed superset, meaning that it adds rules about how different kinds of values can be used.
The earlier error about obj.heigth was not a syntax error: it is an error of using some kind of value (a type) in an incorrect way.
As another example, this is JavaScript code that you can run in your browser, and it will log a value:
-console.log(4 / []);
-
+console.log(4 / []);
+
This syntactically-legal program logs Infinity.
TypeScript, though, considers division of number by an array to be a nonsensical operation, and will issue an error:
// @errors: 2363
-console.log(4 / []);
-
+// @errors: 2363
+console.log(4 / []);
+
It's possible you really did intend to divide a number by an array, perhaps just to see what happens, but most of the time, though, this is a programming mistake. TypeScript's type checker is designed to allow correct programs through while still catching as many common errors as possible. (Later, we'll learn about settings you can use to configure how strictly TypeScript checks your code.)
If you move some code from a JavaScript file to a TypeScript file, you might see type errors depending on how the code is written. These may be legitimate problems with the code, or TypeScript being overly conservative. Throughout this guide we'll demonstrate how to add various TypeScript syntax to eliminate such errors.
-Runtime Behavior
+Runtime Behavior
TypeScript is also a programming language that preserves the runtime behavior of JavaScript.
For example, dividing by zero in JavaScript produces Infinity instead of throwing a runtime exception.
As a principle, TypeScript never changes the runtime behavior of JavaScript code.
Runtime Behavior
specification. (Since the immediately preceding text was raving about how JS code can be used in TS.) --> -Erased Types
+Erased Types
Roughly speaking, once TypeScript's compiler is done with checking your code, it erases the types to produce the resulting "compiled" code. This means that once your code is compiled, the resulting plain JS code has no type information.
This also means that TypeScript never changes the behavior of your program based on the types it inferred. @@ -123,7 +126,7 @@
Erased Types
with an example --- something like `?.` would be good in showing readers that this document is maintained.) --> -Learning JavaScript and TypeScript
+Learning JavaScript and TypeScript
We frequently see the question "Should I learn JavaScript or TypeScript?".
The answer is that you can't learn TypeScript without learning JavaScript! TypeScript shares syntax and runtime behavior with JavaScript, so anything you learn about JavaScript is helping you learn TypeScript at the same time.
@@ -132,7 +135,7 @@Learning JavaScript and TypeScript
If you find yourself searching for something like "how to sort a list in TypeScript", remember: TypeScript is JavaScript's runtime with a compile-time type checker. The way you sort a list in TypeScript is the same way you do so in JavaScript. If you find a resource that uses TypeScript directly, that's great too, but don't limit yourself to thinking you need TypeScript-specific answers for everyday questions about how to accomplish runtime tasks.
-Next Steps
+Next Steps
This was a brief overview of the syntax and tools used in everyday TypeScript. From here, you can:
-
@@ -173,7 +176,7 @@
Next Steps
TypeScript for Functional Programmers
+TypeScript for Functional Programmers
This introduction does not cover object-oriented programming. In practice, object-oriented programs in TypeScript are similar to those in other popular languages with OO features.
-Prerequisites
+Prerequisites
In this introduction, I assume you know the following:
- How to program in JavaScript, the good parts. @@ -50,8 +50,8 @@
undefinedobject
--
Function syntax includes parameter names. This is pretty hard to get used to!
-
+let fst: (a: any, b: any) => any = (a, b) => a; - -// or more precisely: - -let fst: <T, U>(a: T, b: U) => T = (a, b) => a; -let fst: (a: any, b: any) => any = (a, b) => a; + +// or more precisely: + +let fst: <T, U>(a: T, b: U) => T = (a, b) => a; + -
Object literal type syntax closely mirrors object literal value syntax:
-
+let o: { n: number; xs: object[] } = { n: 1, xs: [] }; -let o: { n: number; xs: object[] } = { n: 1, xs: [] }; + -
[T, T]is a subtype ofT[]. This is different than Haskell, where tuples are not related to lists. "right": "right"
@@ -345,22 +345,22 @@ - Declaration initializers are contextually typed by the
@@ -393,81 +393,81 @@
Contextual typing
properties you'd have in a real program. Altogether, this feature can make TypeScript's inference look a bit like a unifying type inference engine, but it is not. -Type aliases
+Type aliases
Type aliases are mere aliases, just like
-typein Haskell. The compiler will attempt to use the alias name wherever it was used in the source code, but does not always succeed.
+type Size = [number, number]; -let x: Size = [101.1, 999.9]; -type Size = [number, number]; +let x: Size = [101.1, 999.9]; +The closest equivalent to
-newtypeis a tagged intersection:
+type FString = string & { __compileTimeOnly: any }; -type FString = string & { __compileTimeOnly: any }; +An
-FStringis just like a normal string, except that the compiler thinks it has a property named__compileTimeOnlythat doesn't actually exist. This means thatFStringcan still be assigned tostring, but not the other way round.Discriminated Unions
+Discriminated Unions
The closest equivalent to
-datais a union of types with discriminant properties, normally called discriminated unions in TypeScript:
+type Shape = - | { kind: "circle"; radius: number } - | { kind: "square"; x: number } - | { kind: "triangle"; x: number; y: number }; -type Shape = + | { kind: "circle"; radius: number } + | { kind: "square"; x: number } + | { kind: "triangle"; x: number; y: number }; +Unlike Haskell, the tag, or discriminant, is just a property in each object type. Each variant has an identical property with a different unit type. This is still a normal union type; the leading
-|is an optional part of the union type syntax. You can discriminate the members of the union using normal JavaScript code:
+type Shape = - | { kind: "circle"; radius: number } - | { kind: "square"; x: number } - | { kind: "triangle"; x: number; y: number }; - -function area(s: Shape) { - if (s.kind === "circle") { - return Math.PI * s.radius * s.radius; - } else if (s.kind === "square") { - return s.x * s.x; - } else { - return (s.x * s.y) / 2; - } -} -type Shape = + | { kind: "circle"; radius: number } + | { kind: "square"; x: number } + | { kind: "triangle"; x: number; y: number }; + +function area(s: Shape) { + if (s.kind === "circle") { + return Math.PI * s.radius * s.radius; + } else if (s.kind === "square") { + return s.x * s.x; + } else { + return (s.x * s.y) / 2; + } +} +Note that the return type of
areais inferred to benumberbecause TypeScript knows the function is total. If some variant is not covered, the return type ofareawill benumber | undefinedinstead.Also, unlike Haskell, common properties show up in any union, so you can usefully discriminate multiple members of the union:
-
-type Shape = - | { kind: "circle"; radius: number } - | { kind: "square"; x: number } - | { kind: "triangle"; x: number; y: number }; -// ---cut--- -function height(s: Shape) { - if (s.kind === "circle") { - return 2 * s.radius; - } else { - // s.kind: "square" | "triangle" - return s.x; - } -} -Type Parameters
+
+type Shape = + | { kind: "circle"; radius: number } + | { kind: "square"; x: number } + | { kind: "triangle"; x: number; y: number }; +// ---cut--- +function height(s: Shape) { + if (s.kind === "circle") { + return 2 * s.radius; + } else { + // s.kind: "square" | "triangle" + return s.x; + } +} +Type Parameters
Like most C-descended languages, TypeScript requires declaration of type parameters:
-
+function liftArray<T>(t: T): Array<T> { - return [t]; -} -function liftArray<T>(t: T): Array<T> { + return [t]; +} +There is no case requirement, but type parameters are conventionally single uppercase letters. Type parameters can also be constrained to a type, which behaves a bit like type class constraints:
-
+function firstish<T extends { length: number }>(t1: T, t2: T): T { - return t1.length > t2.length ? t1 : t2; -} -function firstish<T extends { length: number }>(t1: T, t2: T): T { + return t1.length > t2.length ? t1 : t2; +} +TypeScript can usually infer type arguments from a call based on the type of the arguments, so type arguments are usually not needed.
Because TypeScript is structural, it doesn't need type parameters as @@ -475,89 +475,89 @@
Type Parameters
function polymorphic. Type parameters should only be used to propagate type information, such as constraining parameters to be the same type: -
+function length<T extends ArrayLike<unknown>>(t: T): number {} - -function length(t: ArrayLike<unknown>): number {} -function length<T extends ArrayLike<unknown>>(t: T): number {} + +function length(t: ArrayLike<unknown>): number {} +In the first
-length, T is not necessary; notice that it's only referenced once, so it's not being used to constrain the type of the return value or other parameters.Higher-kinded types
+Higher-kinded types
TypeScript does not have higher kinded types, so the following is not legal:
-
-function length<T extends ArrayLike<unknown>, U>(m: T<U>) {} -Point-free programming
+
+function length<T extends ArrayLike<unknown>, U>(m: T<U>) {} +Point-free programming
Point-free programming — heavy use of currying and function composition — is possible in JavaScript, but can be verbose. In TypeScript, type inference often fails for point-free programs, so you'll end up specifying type parameters instead of value parameters. The result is so verbose that it's usually better to avoid point-free programming.
-Module system
+Module system
JavaScript's modern module syntax is a bit like Haskell's, except that any file with
-importorexportis implicitly a module:
+import { value, Type } from "npm-package"; -import { other, Types } from "./local-package"; -import * as prefix from "../lib/third-package"; -import { value, Type } from "npm-package"; +import { other, Types } from "./local-package"; +import * as prefix from "../lib/third-package"; +You can also import commonjs modules — modules written using node.js' module system:
-
+import f = require("single-function-package"); -import f = require("single-function-package"); +You can export with an export list:
-
+export { f }; - -function f() { - return g(); -} -function g() {} // g is not exported -export { f }; + +function f() { + return g(); +} +function g() {} // g is not exported +Or by marking each export individually:
-
+export function f() { return g() } -function g() { } -export function f() { return g() } +function g() { } +The latter style is more common but both are allowed, even in the same file.
-
+readonlyandconstreadonlyandconstIn JavaScript, mutability is the default, although it allows variable declarations with
-constto declare that the reference is immutable. The referent is still mutable:
+const a = [1, 2, 3]; -a.push(102); // ): -a[0] = 101; // D: -const a = [1, 2, 3]; +a.push(102); // ): +a[0] = 101; // D: +TypeScript additionally has a
-readonlymodifier for properties.
+interface Rx { - readonly x: number; -} -let rx: Rx = { x: 1 }; -rx.x = 12; // error -interface Rx { + readonly x: number; +} +let rx: Rx = { x: 1 }; +rx.x = 12; // error +It also ships with a mapped type
-Readonly<T>that makes all propertiesreadonly:
+interface X { - x: number; -} -let rx: Readonly<X> = { x: 1 }; -rx.x = 12; // error -interface X { + x: number; +} +let rx: Readonly<X> = { x: 1 }; +rx.x = 12; // error +And it has a specific
-ReadonlyArray<T>type that removes side-affecting methods and prevents writing to indices of the array, as well as special syntax for this type:
+let a: ReadonlyArray<number> = [1, 2, 3]; -let b: readonly number[] = [1, 2, 3]; -a.push(102); // error -b[0] = 101; // error -let a: ReadonlyArray<number> = [1, 2, 3]; +let b: readonly number[] = [1, 2, 3]; +a.push(102); // error +b[0] = 101; // error +You can also use a const-assertion, which operates on arrays and object literals:
-
+let a = [1, 2, 3] as const; -a.push(102); // error -a[0] = 101; // error -let a = [1, 2, 3] as const; +a.push(102); // error +a[0] = 101; // error +However, none of these options are the default, so they are not consistently used in TypeScript code.
-Next Steps
+Next Steps
This doc is a high level overview of the syntax and types you would use in everyday code. From here you should:
- Read the full Handbook from start to finish @@ -566,7 +566,7 @@
- Read the full Handbook from start to finish diff --git a/dist/The Handbook/index.html b/dist/The Handbook/index.html index c99d607..cd32b7d 100644 --- a/dist/The Handbook/index.html +++ b/dist/The Handbook/index.html @@ -19,15 +19,15 @@
-
@@ -47,12 +47,12 @@
How is this Handbook Structured
The reference section below the handbook in the navigation is built to provide a richer understanding of how a particular part of TypeScript works. You can read it top-to-bottom, but each section aims to provide a deeper explanation of a single concept - meaning there is no aim for continuity.
- TypeScript for the New Programmer @@ -64,7 +64,7 @@
Next Steps
diff --git a/dist/TS for OOPers/index.html b/dist/TS for OOPers/index.html index f807ede..7c18019 100644 --- a/dist/TS for OOPers/index.html +++ b/dist/TS for OOPers/index.html @@ -19,108 +19,108 @@On this page
-[[toc]]
+- TypeScript for Java/C# Programmers
+TypeScript for Java/C# Programmers
TypeScript stands in an unusual relationship to JavaScript. TypeScript offers all of JavaScript's features, and an additional layer on top of these: TypeScript's type system.
For example, JavaScript provides language primitives like
stringandnumber, but it doesn't check that you've consistently assigned these. TypeScript does.This means that your existing working JavaScript code is also TypeScript code. The main benefit of TypeScript is that it can highlight unexpected behavior in your code, lowering the chance of bugs.
This tutorial provides a brief overview of TypeScript, focusing on its type system.
-Types by Inference
+Types by Inference
TypeScript knows the JavaScript language and will generate types for you in many cases. For example in creating a variable and assigning it to a particular value, TypeScript will use the value as its type.
-
+let helloWorld = "Hello World"; -// ^? -let helloWorld = "Hello World"; +// ^? +By understanding how JavaScript works, TypeScript can build a type-system that accepts JavaScript code but has types. This offers a type-system without needing to add extra characters to make types explicit in your code. That's how TypeScript knows that
helloWorldis astringin the above example.You may have written JavaScript in Visual Studio Code, and had editor auto-completion. Visual Studio Code uses TypeScript under the hood to make it easier to work with JavaScript.
-Defining Types
+Defining Types
You can use a wide variety of design patterns in JavaScript. However, some design patterns make it difficult for types to be inferred automatically (for example, patterns that use dynamic programming). To cover these cases, TypeScript supports an extension of the JavaScript language, which offers places for you to tell TypeScript what the types should be.
For example, to create an object with an inferred type which includes
-name: stringandid: number, you can write:
+const user = { - name: "Hayes", - id: 0, -}; -const user = { + name: "Hayes", + id: 0, +}; +You can explicitly describe this object's shape using an
-interfacedeclaration:
+interface User { - name: string; - id: number; -} -interface User { + name: string; + id: number; +} +You can then declare that a JavaScript object conforms to the shape of your new
-interfaceby using syntax like: TypeNameafter a variable declaration:
+interface User { - name: string; - id: number; -} -// ---cut--- -const user: User = { - name: "Hayes", - id: 0, -}; -interface User { + name: string; + id: number; +} +// ---cut--- +const user: User = { + name: "Hayes", + id: 0, +}; +If you provide an object that doesn't match the interface you have provided, TypeScript will warn you:
-
+// @errors: 2322 -interface User { - name: string; - id: number; -} - -const user: User = { - username: "Hayes", - id: 0, -}; -// @errors: 2322 +interface User { + name: string; + id: number; +} + +const user: User = { + username: "Hayes", + id: 0, +}; +Since JavaScript supports classes and object-oriented programming, so does TypeScript. You can use an interface declaration with classes:
-
+interface User { - name: string; - id: number; -} - -class UserAccount { - name: string; - id: number; - - constructor(name: string, id: number) { - this.name = name; - this.id = id; - } -} - -const user: User = new UserAccount("Murphy", 1); -interface User { + name: string; + id: number; +} + +class UserAccount { + name: string; + id: number; + + constructor(name: string, id: number) { + this.name = name; + this.id = id; + } +} + +const user: User = new UserAccount("Murphy", 1); +You can use interfaces to annotate parameters and return values to functions:
-
+// @noErrors -interface User { - name: string; - id: number; -} -// ---cut--- -function deleteUser(user: User) { - // ... -} - -function getAdminUser(): User { - //... -} -// @noErrors +interface User { + name: string; + id: number; +} +// ---cut--- +function deleteUser(user: User) { + // ... +} + +function getAdminUser(): User { + //... +} +There is already a small set of primitive types available in JavaScript:
boolean,bigint,null,number,string,symbol, andundefined, which you can use in an interface. TypeScript extends this list with a few more, such asany(allow anything),unknown(ensure someone using this type declares what the type is),never(it's not possible that this type could happen), andvoid(a function which returnsundefinedor has no return value).You'll see that there are two syntaxes for building types: Interfaces and Types. You should prefer
-interface. Usetypewhen you need specific features.Composing Types
+Composing Types
With TypeScript, you can create complex types by combining simple ones. There are two popular ways to do so: with unions, and with generics.
-Unions
+Unions
With a union, you can declare that a type could be one of many types. For example, you can describe a
-booleantype as being eithertrueorfalse:
+type MyBool = true | false; -type MyBool = true | false; +Note: If you hover over
MyBoolabove, you'll see that it is classed asboolean. That's a property of the Structural Type System. More on this below.A popular use-case for union types is to describe the set of
-stringornumberliterals that a value is allowed to be:
+type WindowStates = "open" | "closed" | "minimized"; -type LockStates = "locked" | "unlocked"; -type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9; -type WindowStates = "open" | "closed" | "minimized"; +type LockStates = "locked" | "unlocked"; +type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9; +Unions provide a way to handle different types too. For example, you may have a function that takes an
-arrayor astring:
+function getLength(obj: string | string[]) { - return obj.length; -} -function getLength(obj: string | string[]) { + return obj.length; +} +To learn the type of a variable, use
typeof:@@ -158,100 +158,100 @@
Unions
For example, you can make a function return different values depending on whether it is passed a string or an array:
-
-function wrapInArray(obj: string | string[]) { - if (typeof obj === "string") { - return [obj]; -// ^? - } - return obj; -} -Generics
+
+function wrapInArray(obj: string | string[]) { + if (typeof obj === "string") { + return [obj]; +// ^? + } + return obj; +} +Generics
Generics provide variables to types. A common example is an array. An array without generics could contain anything. An array with generics can describe the values that the array contains.
-
+type StringArray = Array<string>; -type NumberArray = Array<number>; -type ObjectWithNameArray = Array<{ name: string }>; -type StringArray = Array<string>; +type NumberArray = Array<number>; +type ObjectWithNameArray = Array<{ name: string }>; +You can declare your own types that use generics:
-
-// @errors: 2345 -interface Backpack<Type> { - add: (obj: Type) => void; - get: () => Type; -} - -// This line is a shortcut to tell TypeScript there is a -// constant called `backpack`, and to not worry about where it came from. -declare const backpack: Backpack<string>; - -// object is a string, because we declared it above as the variable part of Backpack. -const object = backpack.get(); - -// Since the backpack variable is a string, you can't pass a number to the add function. -backpack.add(23); -Structural Type System
+
+// @errors: 2345 +interface Backpack<Type> { + add: (obj: Type) => void; + get: () => Type; +} + +// This line is a shortcut to tell TypeScript there is a +// constant called `backpack`, and to not worry about where it came from. +declare const backpack: Backpack<string>; + +// object is a string, because we declared it above as the variable part of Backpack. +const object = backpack.get(); + +// Since the backpack variable is a string, you can't pass a number to the add function. +backpack.add(23); +Structural Type System
One of TypeScript's core principles is that type checking focuses on the shape that values have. This is sometimes called "duck typing" or "structural typing".
In a structural type system, if two objects have the same shape, they are considered to be of the same type.
-
+interface Point { - x: number; - y: number; -} - -function logPoint(p: Point) { - console.log(`${p.x}, ${p.y}`); -} - -// logs "12, 26" -const point = { x: 12, y: 26 }; -logPoint(point); -interface Point { + x: number; + y: number; +} + +function logPoint(p: Point) { + console.log(`${p.x}, ${p.y}`); +} + +// logs "12, 26" +const point = { x: 12, y: 26 }; +logPoint(point); +The
pointvariable is never declared to be aPointtype. However, TypeScript compares the shape ofpointto the shape ofPointin the type-check. They have the same shape, so the code passes.The shape-matching only requires a subset of the object's fields to match.
-
+// @errors: 2345 -interface Point { - x: number; - y: number; -} - -function logPoint(p: Point) { - console.log(`${p.x}, ${p.y}`); -} -// ---cut--- -const point3 = { x: 12, y: 26, z: 89 }; -logPoint(point3); // logs "12, 26" - -const rect = { x: 33, y: 3, width: 30, height: 80 }; -logPoint(rect); // logs "33, 3" - -const color = { hex: "#187ABF" }; -logPoint(color); -// @errors: 2345 +interface Point { + x: number; + y: number; +} + +function logPoint(p: Point) { + console.log(`${p.x}, ${p.y}`); +} +// ---cut--- +const point3 = { x: 12, y: 26, z: 89 }; +logPoint(point3); // logs "12, 26" + +const rect = { x: 33, y: 3, width: 30, height: 80 }; +logPoint(rect); // logs "33, 3" + +const color = { hex: "#187ABF" }; +logPoint(color); +There is no difference between how classes and objects conform to shapes:
-
+// @errors: 2345 -interface Point { - x: number; - y: number; -} - -function logPoint(p: Point) { - console.log(`${p.x}, ${p.y}`); -} -// ---cut--- -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" -// @errors: 2345 +interface Point { + x: number; + y: number; +} + +function logPoint(p: Point) { + console.log(`${p.x}, ${p.y}`); +} +// ---cut--- +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" +If the object or class has all the required properties, TypeScript will say they match, regardless of the implementation details.
-Next Steps
+Next Steps
This was a brief overview of the syntax and tools used in everyday TypeScript. From here, you can:
- diff --git a/dist/_includes/home.njk b/dist/_includes/home.njk index 51c30d9..8a3dbeb 100644 --- a/dist/_includes/home.njk +++ b/dist/_includes/home.njk @@ -7,71 +7,91 @@The TypeScript Handbook
+The TypeScript Handbook
-About this Handbook
+About this Handbook
Over 20 years after its introduction to the programming community, JavaScript is now one of the most widespread cross-platform languages ever created. Starting as a small scripting language for adding trivial interactivity to webpages, JavaScript has grown to be a language of choice for both frontend and backend applications of every size. While the size, scope, and complexity of programs written in JavaScript has grown exponentially, the ability of the JavaScript language to express the relationships between different units of code has not. Combined with JavaScript's rather peculiar runtime semantics, this mismatch between language and program complexity has made JavaScript development a difficult task to manage at scale.
The most common kinds of errors that programmers write can be described as type errors: a certain kind of value was used where a different kind of value was expected. This could be due to simple typos, a failure to understand the API surface of a library, incorrect assumptions about runtime behavior, or other errors. The goal of TypeScript is to be a static typechecker for JavaScript programs - in other words, a tool that runs before your code runs (static) and ensures that the types of the program are correct (typechecked).
If you are coming to TypeScript without a JavaScript background, with the intention of TypeScript being your first language, we recommend you first start reading the documentation on either the Microsoft Learn JavaScript tutorial or read JavaScript at the Mozilla Web Docs. If you have experience in other languages, you should be able to pick up JavaScript syntax quite quickly by reading the handbook.
-How is this Handbook Structured
+How is this Handbook Structured
The handbook is split into two sections:
Non-Goals
+Non-Goals
The Handbook is also intended to be a concise document that can be comfortably read in a few hours. Certain topics won't be covered in order to keep things short.
Specifically, the Handbook does not fully introduce core JavaScript basics like functions, classes, and closures. Where appropriate, we'll include links to background reading that you can use to read up on those concepts.
The Handbook also isn't intended to be a replacement for a language specification. In some cases, edge cases or formal descriptions of behavior will be skipped in favor of high-level, easier-to-understand explanations. Instead, there are separate reference pages that more precisely and formally describe many aspects of TypeScript's behavior. The reference pages are not intended for readers unfamiliar with TypeScript, so they may use advanced terminology or reference topics you haven't read about yet.
Finally, the Handbook won't cover how TypeScript interacts with other tools, except where necessary. Topics like how to configure TypeScript with webpack, rollup, parcel, react, babel, closure, lerna, rush, bazel, preact, vue, angular, svelte, jquery, yarn, or npm are out of scope - you can find these resources elsewhere on the web.
-Get Started
+Get Started
Before getting started with The Basics, we recommend reading one of the following introductory pages. These introductions are intended to highlight key similarities and differences between TypeScript and your favored programming language, and clear up common misconceptions specific to those languages.
Get Started
- TypeScript is JavaScript with syntax for types
+TypeScript is JavaScript with syntax for types +
TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.
- -+@@ -120,7 +140,7 @@@@ -133,7 +153,7 @@@@ -148,7 +168,8 @@ -Describe your data
-Describe the shape of your objects and functions in your code.
++ Describe the shape of your objects and functions in your code.
Making it possible to see documentation and issues in your editor.
@@ -174,319 +195,336 @@ --tstypeResult = "pass" | "fail"functionverify (result :Result ) {if (result === "pass") {console .log ("Passed")} else {console .log ("Failed")}}TypeScript file.
-- ---
-tstypeResult = "pass" | "fail"functionverify (result :Result ) {if (result === "pass") {console .log ("Passed")} else {console .log ("Failed")}}Types are removed.
-- ---
-jsfunctionverify (result ) {if (result === "pass") {console .log ("Passed")} else {console .log ("Failed")}}JavaScript file.
-
Prerequisites
The C++ Programming Language is
a good place to learn about C-style type syntax. Unlike C++,
TypeScript uses postfix types, like so: x: string instead of string x.
Concepts not in Haskell
-Built-in types
+Concepts not in Haskell
+Built-in types
JavaScript defines 8 built-in types:
Note that functions and arrays are objects at runtime, but have their own predicates.
-Intersections
+Intersections
In addition to unions, TypeScript also has intersections:
-type Combined = { a: number } & { b: string };
-type Conflicting = { a: number } & { a: string };
-
+type Combined = { a: number } & { b: string };
+type Conflicting = { a: number } & { a: string };
+
Combined has two properties, a and b, just as if they had been
written as one object literal type. Intersection and union are
recursive in case of conflicts, so Conflicting.a: number & string.
Unit types
+Unit types
Unit types are subtypes of primitive types that contain exactly one
primitive value. For example, the string "foo" has the type
"foo". Since JavaScript has no built-in enums, it is common to use a set of
well-known strings instead. Unions of string literal types allow
TypeScript to type this pattern:
declare function pad(s: string, n: number, direction: "left" | "right"): string;
-pad("hi", 10, "left");
-
+declare function pad(s: string, n: number, direction: "left" | "right"): string;
+pad("hi", 10, "left");
+
When needed, the compiler widens — converts to a
supertype — the unit type to the primitive type, such as "foo"
to string. This happens when using mutability, which can hamper some
uses of mutable variables:
// @errors: 2345
-declare function pad(s: string, n: number, direction: "left" | "right"): string;
-// ---cut---
-let s = "right";
-pad("hi", 10, s); // error: 'string' is not assignable to '"left" | "right"'
-
+// @errors: 2345
+declare function pad(s: string, n: number, direction: "left" | "right"): string;
+// ---cut---
+let s = "right";
+pad("hi", 10, s); // error: 'string' is not assignable to '"left" | "right"'
+
Here's how the error happens:
Unit types
You can work around this with a type annotation for s, but that
in turn prevents assignments to s of variables that are not of type
"left" | "right".
declare function pad(s: string, n: number, direction: "left" | "right"): string;
-// ---cut---
-let s: "left" | "right" = "right";
-pad("hi", 10, s);
-
-Concepts similar to Haskell
-Contextual typing
+declare function pad(s: string, n: number, direction: "left" | "right"): string;
+// ---cut---
+let s: "left" | "right" = "right";
+pad("hi", 10, s);
+
+Concepts similar to Haskell
+Contextual typing
TypeScript has some obvious places where it can infer types, like variable declarations:
-let s = "I'm a string!";
-
+let s = "I'm a string!";
+
But it also infers types in a few other places that you may not expect if you've worked with other C-syntax languages:
-declare function map<T, U>(f: (t: T) => U, ts: T[]): U[];
-let sns = map((n) => n.toString(), [1, 2, 3]);
-
+declare function map<T, U>(f: (t: T) => U, ts: T[]): U[];
+let sns = map((n) => n.toString(), [1, 2, 3]);
+
Here, n: number in this example also, despite the fact that T and U
have not been inferred before the call. In fact, after [1,2,3] has
been used to infer T=number, the return type of n => n.toString()
@@ -369,16 +369,16 @@
Contextual typing
Note that inference will work in any order, but intellisense will only
work left-to-right, so TypeScript prefers to declare map with the
array first:
declare function map<T, U>(ts: T[], f: (t: T) => U): U[];
-
+declare function map<T, U>(ts: T[], f: (t: T) => U): U[];
+
Contextual typing also works recursively through object literals, and
on unit types that would otherwise be inferred as string or
number. And it can infer return types from context:
declare function run<T>(thunk: (t: T) => void): T;
-let i: { inference: string } = run((o) => {
- o.inference = "INSERT STATE HERE";
-});
-
+declare function run<T>(thunk: (t: T) => void): T;
+let i: { inference: string } = run((o) => {
+ o.inference = "INSERT STATE HERE";
+});
+
The type of o is determined to be { inference: string } because