Post's cover

学习心得:一开始无法感知 TS 的好处,只是觉得编码更加麻烦,现在慢慢能体会到好处,想象你合作时使用他人创建的组件,组件加了类型定义的话,你写代码时知道哪些 props 是可选哪些是必写,写错了还会有报错提示。

H1Intro

TypeScript addresses the challenges of JavaScript, offering a strong and static type system by providing early error detection and improved tooling.

Benefits:

  • Static typing
  • Code completion
  • Refactoring
  • Shorthand notations

H1Basics

H2Static type-checking

Static types systems describe the shapes and behaviors of what our values will be when we run our programs. A type-checker like TypeScript uses that information and tells us when things might be going off the rails.

typescriptconst message = "hello!"; message(); // This expression is not callable. // Type 'String' has no call signatures.

H2Non-exception Failures

A static type system has to make the call over what code should be flagged as an error in its system, even if it’s “valid” JavaScript that won’t immediately throw an error. In TypeScript, the following code produces an error about location not being defined:

typescriptconst user = { name: "Harris", age: 23, }; user.location; // Property 'location' does not exist on type '{ name: string; age: number; }'.

H2Types for Tooling

TypeScript can catch bugs when we make mistakes in our code, and TypeScript can also prevent us from making those mistakes in the first place by providing code completion as you type in the editor.

H2Explicit Types

typescriptfunction greet(person: string, date: Date) { console.log(`Hello ${person}, today is ${date.toDateString()}!`); } greet("Maddison", Date()); // Argument of type 'string' is not assignable to parameter of type 'Date'. // Correct: greet("Maddison", new Date());

Keep in mind, we don’t always have to write explicit type annotations. In many cases, TypeScript can even just infer (or “figure out”) the types for us even if we omit them.

typescriptlet msg = "hello there!"; // let msg: string

H2Erased Types

When we compile the above function greet with tsc to output JavaScript:

js"use strict"; function greet(person, date) { console.log("Hello ".concat(person, ", today is ").concat(date.toDateString(), "!")); } greet("Maddison", new Date());

Type annotations aren’t part of JavaScript (or ECMAScript to be pedantic), so there really aren’t any browsers or other runtimes that can just run TypeScript unmodified. That’s why TypeScript needs a compiler in the first place.

H2Downleveling

Why did rewriting the template string above happen?

Template strings are a feature from a version of ECMAScript called ECMAScript 2015 (a.k.a. ECMAScript 6, ES2015, ES6, etc. - don’t ask). TypeScript has the ability to rewrite code from newer versions of ECMAScript to older ones such as ECMAScript 3 or ECMAScript 5 (a.k.a. ES3 and ES5). This process of moving from a newer or “higher” version of ECMAScript down to an older or “lower” one is sometimes called downleveling.

By default TypeScript targets ES3, an extremely old version of ECMAScript. We could have chosen something a little bit more recent by using the target option. Running with --target es2015 changes TypeScript to target ECMAScript 2015, meaning code should be able to run wherever ECMAScript 2015 is supported.

While the default target is ES3, the great majority of current browsers support ES2015. Most developers can therefore safely specify ES2015 or above as a target, unless compatibility with certain ancient browsers is important.

H1Types

JS:

  • number
  • string
  • boolean
  • null
  • undefined
  • object

TS:

  • any
  • unknown
  • never
  • enum
  • tuple

H2Tuples

typescriptlet user: [number, string] = [2000, "harris"];

H2Enums

typescriptenum Size { Small = 1, Medium, Large } console.log(Large); // 3, because all of the following members are auto-incremented from that point on.

H2Arrays

typescriptlet arr: numbers[] = [1, 2, 3]; let arr1: Array<number> = [1, 2, 3]; let arr2: string[] = ["harris", "wong"];

H2Functions

Parameter & Return Type Annotations

typescriptfunction sum(a: number, b: number): number { return a + b; } console.log(sum(1, 2)); // 3

H3Functions Which Return Promises

If you want to annotate the return type of a function which returns a promise, you should use the Promise type:

typescriptasync function getFavoriteNumber(): Promise<number> { return 26; }

H2Object Types

typescript// The parameter's type annotation is an object type function printCoord(pt: { x: number; y: number }) { console.log("The coordinate's x value is " + pt.x); console.log("The coordinate's y value is " + pt.y); } printCoord({ x: 3, y: 7 });

H3Optional Properties

Add a ? after the property name.

typescriptfunction printName(obj: { first: string; last?: string }) {/*...*/} // Both OK printName({ first: "Bob" }); printName({ first: "Alice", last: "Alisson" });

H2Union Types

A union type is a type formed from two or more other types, representing values that may be any one of those types.

typescriptfunction printId(id: number | string) { console.log("Your ID is: " + id); } // OK printId(101); // OK printId("202"); // Error printId({ myID: 22342 }); // Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'.

H3Working with Union Types

typescriptfunction 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'. }

The solution is to narrow the union with code.

typescriptfunction printId(id: number | string) { if (typeof id === "string") { // In this branch, id is of type 'string' console.log(id.toUpperCase()); } else { // Here, id is of type 'number' console.log(id); } }

Sometimes you’ll have a union where all the members have something in common. For example:

typescript// Return type is inferred as number[] | string function getFirstThree(x: number[] | string) { return x.slice(0, 3); }

H2Intersection Types

typescripttype Age = {age: number}; type Name = {name: string}; type User = Age & Name; let user: User = { age: 23, name: "Harris" };

H2Type Aliases

it’s common to use the same type more than once and refer to it by a single name.

typescripttype Point = { x: number; y: number; }; type ID = number | string;

H2Interfaces

If you would like a heuristic, use interface until you need to use features from type.

Heuristic: 指得是探索启发式的学习方法,在这里指你有需求了,再去使用 type,也是一种学习新知识的方式。

An interface declaration is another way to name an object type:

typescriptinterface Point { x: number; y: number; }

H3Differences Between Interfaces & Type Aliases

Extending an interface:

Interfaces:

typescriptinterface Animal { name: string; } interface Bear extends Animal { honey: boolean; } const bear = getBear(); bear.name; bear.honey;

Type Aliases:

typescripttype Animal = { name: string; } type Bear = Animal & { honey: boolean; }

Adding new fields to an existing interface, but a type cannot be changed after being created.

typescriptinterface Window { title: string; } interface Window { ts: TypeScriptAPI; }

H2Type Assertions

typescriptconst myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

H2Literal Types

In addition to the general types string and number, we can refer to specific strings and numbers in type positions.

typescriptlet 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.

typescriptfunction 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"'. function compare(a: string, b: string): -1 | 0 | 1 { return a === b ? 0 : a > b ? 1 : -1; }

H3Literal Inference

typescriptdeclare function handleRequest(url: string, method: "GET" | "POST"): void; const req = { url: "https://example.com", method: "GET" }; handleRequest(req.url, req.method); // Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

In the above example req.method is inferred to be string, not "GET".

You can use as const to convert the entire object to be type literals:

const req = { url: "https://example.com", method: "GET" } as const;

H2null and undefined

H3Non-null Assertion Operator (Postfix !)

TypeScript also has a special syntax for removing null and undefined from a type without doing any explicit checking.

typescriptfunction liveDangerously(x?: number | null) { // No error console.log(x!.toFixed()); }

H1Narrowing

H2typeof type guards

  • "string"
  • "number"
  • "bigint"
  • "boolean"
  • "symbol"
  • "undefined"
  • "object"
  • "function"

H2The in operator narrowing

typescripttype Fish = { swim: () => void }; type Bird = { fly: () => void }; function move(animal: Fish | Bird) { if ("swim" in animal) { return animal.swim(); } return animal.fly(); }

H2Discriminated unions

typescriptinterface Shape { kind: "circle" | "square"; radius?: number; sideLength?: number; }

Updated Version:

typescriptinterface Circle { kind: "circle"; radius: number; } interface Square { kind: "square"; sideLength: number; } type Shape = Circle | Square; function getArea(shape: Shape) { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.sideLength ** 2; } }

H1More on Functions

H2Function Type Expressions

typescriptfunction greeter(fn: (a: string) => void) { fn("Hello, World"); } function printToConsole(s: string) { console.log(s); } greeter(printToConsole); // Of course, we can use a type alias to name a function type: type GreetFunction = (a: string) => void; function greeter(fn: GreetFunction) { // ... }

H1TSX

When you want to assign a React component type to a property of a TypeScript object, you can use React.ReactNode as the type of property.

typescripttype MyObject = { icon: React.ReactNode; }; // or import { ReactNode } from "react"; type MyObject = { icon: ReactNode; };

H1References

Videos

Docs

Prev

Next

Related Posts