零基础学 TypeScript:快速上手教程 – wiki词典

零基础学 TypeScript:快速上手教程

引言

欢迎来到 TypeScript 的世界!如果你是 JavaScript 开发者,或者对编程有一定了解,并希望编写更健壮、更易维护的代码,那么 TypeScript 绝对值得你投入时间学习。

什么是 TypeScript?

TypeScript 是 JavaScript 的一个超集,它添加了静态类型定义。这意味着你可以在编写代码时指定变量、函数参数和返回值的类型。最终,TypeScript 代码会被编译成纯 JavaScript,因此它可以在任何支持 JavaScript 的地方运行。

为什么要学习 TypeScript?

  1. 更少的 Bug:静态类型检查能在代码运行前发现许多潜在错误,减少运行时 Bug。
  2. 更好的可维护性:类型定义让代码结构更清晰,易于理解和重构,尤其是在大型项目中。
  3. 增强的开发体验:现代 IDE(如 VS Code)能利用类型信息提供智能提示、自动补全和即时错误反馈,大大提高开发效率。
  4. 更好的团队协作:明确的类型约定有助于团队成员理解彼此的代码,减少沟通成本。
  5. 拥抱未来:TypeScript 已经成为前端和后端(Node.js)开发中越来越主流的技术。

安装与配置

开始学习 TypeScript 非常简单,你只需要 Node.js 和 npm(或 yarn)。

  1. 安装 Node.js 和 npm
    如果你的机器上还没有安装 Node.js,请前往 Node.js 官网 下载并安装。安装 Node.js 会同时安装 npm。

  2. 全局安装 TypeScript
    打开你的终端或命令行工具,运行以下命令:
    bash
    npm install -g typescript

    验证安装是否成功:
    bash
    tsc -v

    这会显示 TypeScript 编译器的版本号。

  3. 创建你的第一个 TypeScript 项目
    新建一个文件夹,进入该文件夹,然后初始化一个 package.json 文件:
    bash
    mkdir my-ts-project
    cd my-ts-project
    npm init -y

    安装 TypeScript 作为开发依赖:
    bash
    npm install --save-dev typescript

    生成 TypeScript 配置文件 tsconfig.json
    bash
    npx tsc --init

    tsconfig.json 是 TypeScript 项目的核心配置文件,你可以根据项目需求调整其中的选项,例如编译目标(target)、模块系统(module)等。对于初学者,默认配置通常就足够了。

基础语法

类型注解 (Type Annotations)

TypeScript 最核心的特性就是类型注解。你可以在变量、函数参数和函数返回值后面使用 : 来指定类型。

“`typescript
// 变量类型注解
let age: number = 30;
let name: string = “Alice”;
let isStudent: boolean = false;

// 函数参数类型注解
function greet(personName: string) {
console.log(Hello, ${personName}!);
}
greet(name); // Hello, Alice!

// 函数返回值类型注解
function add(a: number, b: number): number {
return a + b;
}
let sum: number = add(5, 3); // sum will be 8
“`

基本数据类型 (Primitive Types)

TypeScript 支持所有 JavaScript 的基本数据类型,并为它们提供了类型注解。

  • number:所有数字,包括整数和浮点数。
    typescript
    let price: number = 99.99;
  • string:所有字符串。
    typescript
    let message: string = "Hello TypeScript!";
  • booleantruefalse
    typescript
    let isActive: boolean = true;
  • nullundefined:在严格模式下,它们只能被赋值给各自的类型或 any 类型。
    typescript
    let n: null = null;
    let u: undefined = undefined;
  • symbol:ES6 中新增的原始类型,表示独一无二的值。
    typescript
    const sym: symbol = Symbol('key');
  • bigint:ES2020 中新增的原始类型,用于处理大整数。
    typescript
    let bigNumber: bigint = 100n;

数组 (Arrays)

定义数组有两种方式:

  1. 类型[]
    typescript
    let numbers: number[] = [1, 2, 3, 4];
    let names: string[] = ["Alice", "Bob"];
  2. Array<类型>(泛型数组):
    typescript
    let scores: Array<number> = [90, 85, 95];

元组 (Tuples)

元组表示一个已知元素数量和类型的数组,各元素的类型不必相同。

typescript
// 定义一个元组,第一个元素是字符串,第二个是数字
let user: [string, number];
user = ["Alice", 25]; // 正确
// user = [25, "Alice"]; // 错误:类型顺序不匹配
// user = ["Bob", 30, true]; // 错误:元素数量不匹配

枚举 (Enums)

枚举允许你定义一组命名的常量。

“`typescript
enum Color {
Red, // 默认为 0
Green, // 默认为 1
Blue // 默认为 2
}

let c: Color = Color.Green; // c 的值为 1
console.log(Color[1]); // 输出: Green

// 你也可以手动赋值
enum StatusCode {
Success = 200,
NotFound = 404,
ServerError = 500
}

let status: StatusCode = StatusCode.Success; // status 的值为 200
“`

Any, Unknown, Void, Never

  • any:表示可以是任何类型。当你不确定某个变量的类型时,可以使用 any。但过度使用 any 会失去 TypeScript 的类型优势。
    typescript
    let someValue: any = "this is a string";
    someValue = 100; // 可以重新赋值为其他类型
    someValue.toFixed(); // 不会报错,但运行时可能出错
  • unknown:与 any 类似,但更安全。unknown 类型的值不能直接进行操作,必须先进行类型检查。
    typescript
    let unknownValue: unknown = "hello";
    // unknownValue.toUpperCase(); // 错误:对象是 'unknown'
    if (typeof unknownValue === 'string') {
    console.log(unknownValue.toUpperCase()); // 正确:现在知道是字符串了
    }
  • void:表示没有任何类型。通常用于函数没有返回值的情况。
    typescript
    function warnUser(): void {
    console.log("This is a warning message.");
    }
  • never:表示那些永远不会返回的函数,或者总是抛出异常的函数。
    “`typescript
    function error(message: string): never {
    throw new Error(message);
    }

    function infiniteLoop(): never {
    while (true) {}
    }
    “`

函数 (Functions)

TypeScript 为函数增加了类型检查的能力。

函数类型 (Function Types)

可以为函数定义类型,包括参数类型和返回值类型。

“`typescript
// 普通函数定义
function multiply(x: number, y: number): number {
return x * y;
}

// 箭头函数定义
const subtract = (x: number, y: number): number => {
return x – y;
};

// 定义函数类型
let myOperation: (arg1: number, arg2: number) => number;

myOperation = multiply; // 正确
// myOperation = subtract; // 也正确

console.log(myOperation(10, 5)); // 50
“`

可选参数与默认参数 (Optional and Default Parameters)

  • 可选参数:使用 ? 标记参数为可选。可选参数必须在所有必选参数之后。
    “`typescript
    function buildName(firstName: string, lastName?: string): string {
    if (lastName) {
    return firstName + ” ” + lastName;
    } else {
    return firstName;
    }
    }

    console.log(buildName(“Bob”)); // Bob
    console.log(buildName(“Bob”, “Adams”)); // Bob Adams
    * **默认参数**:为参数提供默认值。typescript
    function showMessage(text: string, sender: string = “System”): void {
    console.log(${sender}: ${text});
    }

    showMessage(“Hello there!”); // System: Hello there!
    showMessage(“How are you?”, “Alice”); // Alice: How are you?
    “`

函数重载 (Function Overloading)

函数重载允许你为同一个函数提供多个函数签名,以处理不同类型的输入。

“`typescript
// 重载签名 (只提供类型,没有实现)
function combine(a: number, b: number): number;
function combine(a: string, b: string): string;
function combine(a: string, b: number): string; // 新增重载签名

// 实现签名 (必须兼容所有重载签名)
function combine(a: any, b: any): any {
if (typeof a === ‘string’ || typeof b === ‘string’) {
return a.toString() + b.toString();
}
return a + b;
}

console.log(combine(1, 2)); // 3 (number)
console.log(combine(“Hello”, “World”)); // HelloWorld (string)
console.log(combine(“Age: “, 30)); // Age: 30 (string)
“`

接口 (Interfaces)

接口是 TypeScript 中定义对象结构的重要方式,它只关注值的外形,也就是结构化类型系统(或鸭式辨型法 “duck typing”)的核心原则。

定义对象形状 (Defining Object Shapes)

“`typescript
interface Point {
x: number;
y: number;
}

function printPoint(p: Point): void {
console.log(X: ${p.x}, Y: ${p.y});
}

let p1 = { x: 10, y: 20 };
printPoint(p1); // 正确

let p2 = { x: 10, y: 20, z: 30 }; // 多余属性在某些情况下也是允许的,因为它至少满足了 Point 接口的必要属性
printPoint(p2); // 正确

// let p3 = { x: 10 }; // 错误:缺少属性 ‘y’
// printPoint(p3);
“`

可选属性 (Optional Properties)

接口中的属性也可以是可选的,使用 ? 标记。

“`typescript
interface SquareConfig {
color?: string;
width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
let newSquare = { color: “white”, area: 100 };
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}

let mySquare = createSquare({ color: “black” });
let mySquare2 = createSquare({});
“`

只读属性 (Readonly Properties)

使用 readonly 关键字来指定属性为只读,在对象创建后就不能再修改。

“`typescript
interface ReadonlyPoint {
readonly x: number;
readonly y: number;
}

let rp: ReadonlyPoint = { x: 10, y: 20 };
// rp.x = 5; // 错误:Cannot assign to ‘x’ because it is a read-only property.
“`

函数类型接口 (Function Type Interfaces)

接口也可以描述函数类型。

“`typescript
interface SearchFunc {
(source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
let result = src.search(sub);
return result > -1;
}

console.log(mySearch(“hello world”, “world”)); // true
“`

可索引类型接口 (Indexable Type Interfaces)

索引签名描述了那些可以通过索引访问的类型,比如 a[10]a["foo"]

“`typescript
interface StringArray {
[index: number]: string; // 索引签名:索引是数字,值是字符串
}

let myArray: StringArray;
myArray = [“Bob”, “Fred”];

let firstItem: string = myArray[0]; // firstItem 的类型是 string
“`

类 (Classes)

TypeScript 完全支持 ES6 的类,并在此基础上增加了类型检查和修饰符。

基本用法 (Basic Usage)

“`typescript
class Greeter {
greeting: string; // 属性

constructor(message: string) { // 构造函数
    this.greeting = message;
}

greet(): string { // 方法
    return "Hello, " + this.greeting;
}

}

let greeter = new Greeter(“world”);
console.log(greeter.greet()); // Hello, world
“`

继承 (Inheritance)

使用 extends 关键字实现继承。

``typescript
class Animal {
name: string;
constructor(theName: string) { this.name = theName; }
move(distanceInMeters: number = 0) {
console.log(
${this.name} moved ${distanceInMeters}m.`);
}
}

class Snake extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 5) {
console.log(“Slithering…”);
super.move(distanceInMeters);
}
}

class Horse extends Animal {
constructor(name: string) { super(name); }
move(distanceInMeters = 45) {
console.log(“Galloping…”);
super.move(distanceInMeters);
}
}

let sam = new Snake(“Sammy the Python”);
let tom: Animal = new Horse(“Tommy the Palomino”); // 多态

sam.move(); // Slithering… Sammy the Python moved 5m.
tom.move(34); // Galloping… Tommy the Palomino moved 34m.
“`

修饰符 (Access Modifiers)

TypeScript 支持 publicprivateprotected 三种访问修饰符。

  • public (默认):在任何地方都可以访问。
  • private:只能在声明它的类内部访问。
  • protected:可以在声明它的类内部和子类中访问。

“`typescript
class Person {
public name: string; // 默认为 public
private age: number;
protected city: string;

constructor(name: string, age: number, city: string) {
    this.name = name;
    this.age = age;
    this.city = city;
}

public getAge(): number {
    return this.age; // 可以在类内部访问 private 属性
}

}

class Employee extends Person {
constructor(name: string, age: number, city: string, department: string) {
super(name, age, city);
this.department = department;
}
department: string;

public getCity(): string {
    return this.city; // 可以在子类内部访问 protected 属性
}

}

let john = new Person(“John”, 30, “New York”);
console.log(john.name); // John (public)
// console.log(john.age); // 错误:’age’ 是 private 属性
console.log(john.getAge()); // 30 (通过 public 方法访问)

let jane = new Employee(“Jane”, 25, “London”, “HR”);
console.log(jane.getCity()); // London (通过子类方法访问 protected 属性)
// console.log(jane.city); // 错误:’city’ 是 protected 属性,只能在类或子类内部访问
“`

抽象类 (Abstract Classes)

抽象类不能直接实例化,只能被继承。它们可以包含抽象方法(不包含具体实现)和具体方法。

“`typescript
abstract class Department {
constructor(public name: string) {}

printName(): void {
    console.log("Department name: " + this.name);
}

abstract printMeeting(): void; // 必须在派生类中实现

}

class AccountingDepartment extends Department {
constructor() {
super(“Accounting and Auditing”); // 在派生类的构造函数中调用基类构造函数
}

printMeeting(): void {
    console.log("The Accounting Department meets each Monday at 10am.");
}

generateReports(): void {
    console.log("Generating accounting reports...");
}

}

// let department = new Department(); // 错误:不能创建抽象类的实例

let accounting = new AccountingDepartment();
accounting.printName(); // Department name: Accounting and Auditing
accounting.printMeeting(); // The Accounting Department meets each Monday at 10am.
accounting.generateReports(); // Generating accounting reports…
“`

泛型 (Generics)

泛型允许你编写可重用的代码,而不需要提前知道所有类型。它们使得代码在处理多种数据类型时保持灵活性和类型安全。

泛型函数 (Generic Functions)

“`typescript
function identity(arg: T): T {
return arg;
}

let output1 = identity(“myString”); // output1 的类型是 string
let output2 = identity(123); // 类型推断,output2 的类型是 number
“`

泛型接口 (Generic Interfaces)

“`typescript
interface GenericIdentityFn {
(arg: T): T;
}

function identityFn(arg: T): T {
return arg;
}

let myIdentity: GenericIdentityFn = identityFn;
console.log(myIdentity(42)); // 42
“`

泛型类 (Generic Classes)

“`typescript
class GenericNumber {
zeroValue: T;
add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

console.log(myGenericNumber.add(myGenericNumber.zeroValue, 10)); // 10

let stringNumeric = new GenericNumber();
stringNumeric.zeroValue = “”;
stringNumeric.add = function(x, y) { return x + y; };

console.log(stringNumeric.add(stringNumeric.zeroValue, “test”)); // test
“`

类型推断与类型兼容性 (Type Inference and Type Compatibility)

类型推断 (Type Inference)

TypeScript 在很多情况下能够自动推断出变量的类型,而无需显式注解。

“`typescript
let x = 3; // x 被推断为 number 类型
// x = “hello”; // 错误:不能将类型 ‘string’ 分配给类型 ‘number’

let arr = [0, 1, null]; // arr 被推断为 (number | null)[]
“`

类型兼容性 (Type Compatibility)

TypeScript 的类型兼容性基于结构化类型系统(或称为鸭式辨型法)。如果两个类型具有相同的结构,那么它们就是兼容的。

“`typescript
interface Named {
name: string;
}

class PersonType {
name: string;
}

let p: Named;
// ok, because of structural typing
p = new PersonType(); // PersonType 实例兼容 Named 接口,因为它们都有一个 ‘name’ 属性

interface Point2D {
x: number;
y: number;
}

interface Point3D {
x: number;
y: number;
z: number;
}

let p2d: Point2D = { x: 1, y: 2 };
let p3d: Point3D = { x: 1, y: 2, z: 3 };

p2d = p3d; // ok, p3d 至少包含了 p2d 的所有属性
// p3d = p2d; // error, p2d 缺少属性 ‘z’
“`

进阶概念 (Advanced Concepts – 简要介绍)

  • 类型断言 (Type Assertions):当你比 TypeScript 更清楚某个变量的类型时,可以使用类型断言。
    typescript
    let someValue: any = "this is a string";
    let strLength: number = (<string>someValue).length; // 方式一
    let strLength2: number = (someValue as string).length; // 方式二(JSX 推荐)
  • 联合类型 (Union Types):表示一个值可以是几种类型之一。
    typescript
    let id: number | string;
    id = 101;
    id = "abc";
    // id = true; // 错误
  • 交叉类型 (Intersection Types):将多个类型合并为一个类型,它拥有所有类型的特性。
    typescript
    interface Person {
    name: string;
    }
    interface Developer {
    code: () => void;
    }
    type FullStackDeveloper = Person & Developer; // 同时具备 Person 和 Developer 的所有属性和方法
  • 模块 (Modules):使用 exportimport 来组织和重用代码。

实战演练 (Practical Exercise)

让我们创建一个简单的 TypeScript 文件并编译它。

  1. my-ts-project 文件夹中创建一个新文件 src/app.ts
    “`typescript
    // src/app.ts
    interface User {
    id: number;
    name: string;
    email?: string;
    }

    function getUserInfo(user: User): string {
    const emailInfo = user.email ? (Email: ${user.email}) : ”;
    return User ID: ${user.id}, Name: ${user.name}${emailInfo};
    }

    const alice: User = {
    id: 1,
    name: “Alice”,
    email: “[email protected]
    };

    const bob: User = {
    id: 2,
    name: “Bob”
    };

    console.log(getUserInfo(alice));
    console.log(getUserInfo(bob));
    “`

  2. 在终端中,运行 TypeScript 编译器:
    bash
    npx tsc

    这会在你的 my-ts-project 目录下生成一个 app.js 文件(如果 tsconfig.json 配置了 outDir,则会在 outDir 指定的目录下生成)。

  3. 运行编译后的 JavaScript 文件:
    bash
    node app.js

    你将看到输出:
    User ID: 1, Name: Alice (Email: [email protected])
    User ID: 2, Name: Bob

    恭喜!你已经成功编写并运行了你的第一个 TypeScript 代码。

总结

本教程带你从零开始,快速上手了 TypeScript 的核心概念,包括:

  • 类型注解:让代码更具可读性和健壮性。
  • 基本数据类型数组元组枚举:构建数据的基石。
  • 函数:定义带类型检查的函数。
  • 接口:描述对象结构。
  • :面向对象编程。
  • 泛型:编写灵活且类型安全的代码。
  • 类型推断与兼容性:TypeScript 的智能之处。

这只是 TypeScript 的冰山一角。要深入学习,你可以探索更多高级类型(如条件类型、映射类型)、装饰器、JSX/TSX 支持、以及与流行框架(如 React, Angular, Vue)的集成。

接下来做什么?

  • 阅读官方文档TypeScript 官网 提供了最全面和最新的信息。
  • 尝试小型项目:将 TypeScript 应用到你的下一个个人项目中。
  • 学习流行框架的 TypeScript 用法:如果你在使用 React, Angular 或 Vue,学习它们如何与 TypeScript 结合使用。
  • 参与社区:加入 TypeScript 相关的社区,提问和分享经验。

祝你在 TypeScript 的学习旅程中一切顺利!

滚动至顶部