面向 JavaScript 程序员的 TypeScript

TypeScript 与 JavaScript 有着不同寻常的关系。TypeScript 提供了 JavaScript 的所有功能,以及在这些功能之上的附加层:TypeScript 的类型系统。

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

例如,JavaScript 提供像 stringnumber 这样的语言基础类型,但它不会检查你是否一致地分配了这些。TypeScript 可以。

¥For example, JavaScript provides language primitives like string and number, but it doesn’t check that you’ve consistently assigned these. TypeScript does.

这意味着你现有的工作 JavaScript 代码也是 TypeScript 代码。TypeScript 的主要好处是它可以高亮代码中的意外行为,从而降低出现错误的可能性。

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

本教程简要概述了 TypeScript,重点介绍了它的类型系统。

¥This tutorial provides a brief overview of TypeScript, focusing on its type system.

推断类型

¥Types by Inference

TypeScript 了解 JavaScript 语言,并会在许多情况下为你生成类型。例如,在创建变量并将其分配给特定值时,TypeScript 将使用该值作为其类型。

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

ts
let helloWorld = "Hello World";
let helloWorld: string
Try

通过了解 JavaScript 的工作原理,TypeScript 可以构建一个接受 JavaScript 代码但具有类型的类型系统。这提供了一个类型系统,无需添加额外的字符来在代码中明确显示类型。在上面的例子中,TypeScript 就是这样知道 helloWorldstring 的。

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

你可能已经在 Visual Studio Code 中编写过 JavaScript,并且具有编辑器自动补齐功能。Visual Studio Code 在底层使用 TypeScript 来简化 JavaScript 的使用。

¥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

你可以在 JavaScript 中使用多种设计模式。但是,某些设计模式使得自动推断类型变得困难(例如,使用动态编程的模式)。为了涵盖这些情况,TypeScript 支持 JavaScript 语言的扩展,它为你提供了告诉 TypeScript 类型应该是什么的地方。

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

例如,要创建一个包含 name: stringid: number 的推断类型的对象,你可以编写:

¥For example, to create an object with an inferred type which includes name: string and id: number, you can write:

ts
const user = {
name: "Hayes",
id: 0,
};
Try

你可以使用 interface 声明明确描述此对象的形状:

¥You can explicitly describe this object’s shape using an interface declaration:

ts
interface User {
name: string;
id: number;
}
Try

然后,你可以在变量声明后使用类似 : TypeName 的语法来声明 JavaScript 对象符合新 interface 的形状:

¥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:

ts
const user: User = {
name: "Hayes",
id: 0,
};
Try

如果你提供的对象与你提供的接口不匹配,TypeScript 会警告你:

¥If you provide an object that doesn’t match the interface you have provided, TypeScript will warn you:

ts
interface User {
name: string;
id: number;
}
 
const user: User = {
username: "Hayes",
Object literal may only specify known properties, and 'username' does not exist in type 'User'.2353Object literal may only specify known properties, and 'username' does not exist in type 'User'.
id: 0,
};
Try

由于 JavaScript 支持类和面向对象编程,因此 TypeScript 也支持。你可以对类使用接口声明:

¥Since JavaScript supports classes and object-oriented programming, so does TypeScript. You can use an interface declaration with classes:

ts
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);
Try

你可以使用接口来注释参数并将值返回给函数:

¥You can use interfaces to annotate parameters and return values to functions:

ts
function deleteUser(user: User) {
// ...
}
 
function getAdminUser(): User {
//...
}
Try

JavaScript 中已经有一小部分基础类型可用:booleanbigintnullnumberstringsymbolundefined,可在接口中使用。TypeScript 用更多的东西扩展了这个列表,比如 any(允许任何东西)、unknown(确保使用这个类型的人声明了这个类型是什么)、never(这个类型不可能发生)和 void(一个函数返回 undefined 或没有返回值)。

¥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).

你会看到构建类型有两种语法:接口和类型。你应该更喜欢 interface。当你需要特定功能时,请使用 type

¥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

使用 TypeScript,你可以通过组合简单类型来创建复杂类型。有两种流行的方法可以做到这一点:联合和泛型。

¥With TypeScript, you can create complex types by combining simple ones. There are two popular ways to do so: unions and generics.

联合

¥Unions

使用联合,你可以声明一个类型可以是多种类型之一。例如,你可以将 boolean 类型描述为 truefalse

¥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:

ts
type MyBool = true | false;
Try

注意:如果将鼠标悬停在上面的 MyBool 上,你会看到它被归类为 boolean。这是结构类型系统的一个属性。更多内容请见下文。

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

联合类型的一个流行用例是描述允许值是的 stringnumber 字面量 的集合:

¥A popular use-case for union types is to describe the set of string or number literals that a value is allowed to be:

ts
type WindowStates = "open" | "closed" | "minimized";
type LockStates = "locked" | "unlocked";
type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
Try

联合也提供了一种处理不同类型的方法。例如,你可能有一个接受 arraystring 的函数:

¥Unions provide a way to handle different types too. For example, you may have a function that takes an array or a string:

ts
function getLength(obj: string | string[]) {
return obj.length;
}
Try

要了解变量的类型,请使用 typeof

¥To learn the type of a variable, use typeof:

类型 谓词
字符串 typeof s === "string"
数字 typeof n === "number"
布尔值 typeof b === "boolean"
undefined typeof undefined === "undefined"
函数 typeof f === "function"
数组 Array.isArray(a)

例如,你可以根据传递给函数的是字符串还是数组来使函数返回不同的值:

¥For example, you can make a function return different values depending on whether it is passed a string or an array:

ts
function wrapInArray(obj: string | string[]) {
if (typeof obj === "string") {
return [obj];
(parameter) obj: string
}
return obj;
}
Try

泛型

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

ts
type StringArray = Array<string>;
type NumberArray = Array<number>;
type ObjectWithNameArray = Array<{ name: string }>;

你可以声明自己的使用泛型的类型:

¥You can declare your own types that use generics:

ts
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);
Argument of type 'number' is not assignable to parameter of type 'string'.2345Argument of type 'number' is not assignable to parameter of type 'string'.
Try

结构类型系统

¥Structural Type System

TypeScript 的核心原则之一是类型检查侧重于值的形状。这有时称为 “duck typing” 或 “结构类型”。

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

ts
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);
Try

point 变量从未声明为 Point 类型。但是,TypeScript 在类型检查中将 point 的形状与 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.

ts
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);
Argument of type '{ hex: string; }' is not assignable to parameter of type 'Point'. Type '{ hex: string; }' is missing the following properties from type 'Point': x, y2345Argument of type '{ hex: string; }' is not assignable to parameter of type 'Point'. Type '{ hex: string; }' is missing the following properties from type 'Point': x, y
Try

类和对象如何符合形状之间没有区别:

¥There is no difference between how classes and objects conform to shapes:

ts
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"
Try

如果对象或类具有所有必需的属性,TypeScript 会说它们匹配,而不管实现细节如何。

¥If the object or class has all the required properties, TypeScript will say they match, regardless of the implementation details.

下一步

¥Next Steps

这是对日常 TypeScript 中使用的语法和工具的简要概述。从这里,你可以:

¥This was a brief overview of the syntax and tools used in everyday TypeScript. From here, you can: