TypeScript这几年越来越火,已经成为了前端开发的标配。不管是React、Vue还是Angular,主流框架都在大力支持TypeScript,很多大厂的前端项目也都已经迁移到了TypeScript。可以说,现在找前端工作,不会TypeScript已经不行了。

TypeScript 5.4在2024年3月正式发布,带来了不少新特性和改进,比如更智能的类型收窄、更好的推断性能、新的实用类型等。如果你还没有学过TypeScript,或者想从最新版本开始学,这篇文章就是为你准备的。

这篇文章是一篇TypeScript 5.4+的入门指南,从零开始,循序渐进,带你了解TypeScript的核心概念和基本用法。文章会配合大量的代码示例,让你不仅能看懂,还能动手实践。读完这篇文章,你应该能掌握TypeScript的基础知识,能够用TypeScript编写简单的项目。

什么是TypeScript

在开始学习之前,先搞清楚什么是TypeScript。

TypeScript是由微软开发的一种开源编程语言,它是JavaScript的超集。简单来说,TypeScript就是在JavaScript的基础上,加上了类型系统和一些新的语法特性。所有的JavaScript代码都是合法的TypeScript代码,但TypeScript代码需要编译成JavaScript才能在浏览器或者Node.js环境中运行。

TypeScript的核心是类型系统。JavaScript是动态类型语言,变量的类型是在运行时确定的,而且可以随时改变。这种灵活性在写小项目的时候很方便,但在大型项目中,很容易因为类型错误而产生bug,而且很多错误要到运行时才能发现,调试成本很高。

TypeScript给JavaScript加上了静态类型系统,在编译时就能检查类型错误,很多bug在写代码的时候就能被发现,大大提高了代码的可靠性和可维护性。而且有了类型信息,IDE的代码提示、自动补全、重构等功能也会强大很多,开发体验大大提升。

简单总结一下,TypeScript的主要优势有:

第一,静态类型检查,在编译时就能发现类型错误,减少运行时bug。 第二,更好的IDE支持,代码提示、自动补全、重构等功能更强大,开发体验更好。 第三,代码更易维护,类型信息就是最好的文档,别人读你的代码的时候,通过类型就能知道函数的参数和返回值是什么。 第四,支持最新的JavaScript特性,甚至一些还在提案阶段的特性,TypeScript也能提前支持,编译成兼容的JavaScript代码。 第五,渐进式采用,可以在现有JavaScript项目中逐步引入TypeScript,不需要一次性全部重写。

当然,TypeScript也不是没有缺点。它有一定的学习成本,特别是对于没有静态类型语言经验的JavaScript开发者来说,需要转变思维方式。而且TypeScript需要编译,增加了构建的复杂度。还有就是写TypeScript需要写更多的类型注解,代码量会比纯JavaScript多一些。但总体来说,对于中大型项目,TypeScript带来的好处远远大于这些成本,这也是为什么它越来越流行的原因。

环境搭建

学习TypeScript的第一步是搭建开发环境。

TypeScript的运行环境很简单,只需要安装Node.js和TypeScript编译器就行。

首先,你需要安装Node.js。如果你还没有安装Node.js,可以去官网(https://nodejs.org)下载安装,建议安装LTS版本。安装完成后,在命令行输入node -vnpm -v,如果能输出版本号,说明安装成功了。

然后,安装TypeScript编译器。有两种安装方式:全局安装和本地安装。

全局安装的方式是在命令行输入:

npm install -g typescript

安装完成后,输入tsc -v,如果能输出版本号,说明安装成功了。全局安装之后,你可以在任何目录下使用tsc命令来编译TypeScript文件。

本地安装的方式是在你的项目目录下输入:

npm install typescript --save-dev

本地安装的TypeScript只会在当前项目中使用,不同项目可以使用不同版本的TypeScript,更灵活。本地安装之后,需要通过npx tsc来调用编译器,或者在package.json的scripts中配置。

对于初学者来说,建议先全局安装,使用起来更简单。等熟悉了之后,再根据项目需要选择本地安装。

安装好TypeScript之后,就可以写第一个TypeScript程序了。

创建一个名为hello.ts的文件,写入以下代码:

function sayHello(name: string): string {
  return `Hello, ${name}!`;
}

const message = sayHello("TypeScript");
console.log(message);

然后在命令行输入:

tsc hello.ts

编译成功后,会在同目录下生成一个hello.js文件。然后运行:

node hello.js

你会看到输出Hello, TypeScript!

恭喜你,第一个TypeScript程序运行成功了!

在实际开发中,我们通常不会手动一个个编译文件,而是使用配置文件。在项目根目录下创建一个tsconfig.json文件,用来配置TypeScript的编译选项。一个最简单的tsconfig.json如下:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

这个配置的意思是:编译目标是ES2020,模块系统是commonjs,开启严格模式,编译输出到dist目录,源代码在src目录下,包含src目录下的所有ts文件。

有了tsconfig.json之后,只需要在项目根目录下输入tsc,就会自动按照配置编译所有的TypeScript文件。

对于前端项目,通常会使用构建工具(比如Vite、Webpack等)来集成TypeScript,不需要手动调用tsc。Vite对TypeScript有原生支持,创建项目的时候选择TypeScript模板就行,非常方便。不过对于初学者来说,建议先用tsc命令手动编译,理解编译过程,再使用构建工具。

基础类型

TypeScript的核心是类型系统,我们先从最基础的类型开始学起。

TypeScript支持JavaScript的所有基本类型,并且在它们的基础上加上了类型注解。

布尔类型

布尔类型只有两个值:true和false。

let isDone: boolean = false;
let hasStarted: boolean = true;

数字类型

TypeScript中的所有数字都是浮点数,类型是number。支持十进制、十六进制、二进制、八进制等写法。

let age: number = 25;
let price: number = 99.99;
let hex: number = 0xff;
let binary: number = 0b1010;

字符串类型

字符串类型用string表示,可以用单引号、双引号或者模板字符串。

let name: string = "张三";
let greeting: string = '你好';
let message: string = `大家好,我是${name},今年${age}岁`;

数组类型

数组有两种写法:一种是在元素类型后面加上[],另一种是使用Array<元素类型>泛型。

let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["张三", "李四", "王五"];
let list: Array<number> = [1, 2, 3];

元组类型

元组是固定长度的数组,每个元素的类型可以不同。

let person: [string, number] = ["张三", 25];
console.log(person[0]); // 张三
console.log(person[1]); // 25

需要注意的是,访问元组元素的时候不能越界,而且给元组赋值的时候,元素的类型和顺序必须匹配。

枚举类型

枚举是TypeScript新增的类型,用来定义一组命名的常量。枚举分为数字枚举和字符串枚举。

数字枚举:

enum Direction {
  Up,
  Down,
  Left,
  Right
}

let dir: Direction = Direction.Up;
console.log(dir); // 0

默认情况下,数字枚举从0开始递增。也可以手动指定值:

enum Direction {
  Up = 1,
  Down,
  Left,
  Right
}

字符串枚举:

enum Color {
  Red = "RED",
  Green = "GREEN",
  Blue = "BLUE"
}

let color: Color = Color.Red;
console.log(color); // RED

Any类型

any类型表示任意类型,赋值为any的变量可以赋值任何类型的值,也可以进行任何操作,TypeScript不会对它进行类型检查。

let anything: any = "hello";
anything = 123;
anything = true;
anything = { name: "张三" };

any类型很灵活,但也很危险,因为它绕过了类型检查,可能会导致运行时错误。在实际开发中,应该尽量避免使用any,除非确实需要。TypeScript的strict模式下,隐式any会报错,需要显式声明。

Unknown类型

unknown类型是TypeScript 3.0引入的,和any类似,也可以赋值任何类型的值。但和any不同的是,unknown类型的变量不能直接进行操作,必须先进行类型收窄(类型检查)之后才能使用。

let value: unknown = "hello";
value = 123;

// 下面这行会报错,因为unknown不能直接操作
// console.log(value.toFixed());

// 需要先进行类型检查
if (typeof value === "number") {
  console.log(value.toFixed(2)); // 123.00
}

unknown比any更安全,因为它强制你在使用之前进行类型检查。在不确定类型的情况下,优先使用unknown而不是any。

Void类型

void类型表示没有任何类型,通常用于函数没有返回值的情况。

function log(message: string): void {
  console.log(message);
}

声明为void的变量只能赋值为undefined或null(strict模式下只能赋值为undefined),没有什么实际意义。

Null和Undefined类型

null和undefined在TypeScript中都有自己的类型,分别是null和undefined。默认情况下,它们是所有类型的子类型,可以赋值给任何类型的变量。但在strict模式下(strictNullChecks),null和undefined只能赋值给它们自己的类型和void类型,不能赋值给其他类型,这样可以避免很多空值错误。

let n: null = null;
let u: undefined = undefined;

// strict模式下,下面这行会报错
// let num: number = null;

Never类型

never类型表示永远不会有返回值的类型,比如函数总是抛出异常,或者函数有无限循环。never是所有类型的子类型,可以赋值给任何类型,但没有任何类型可以赋值给never(除了never自己)。

function throwError(message: string): never {
  throw new Error(message);
}

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

类型推断

TypeScript有类型推断机制,很多时候不需要显式写类型注解,TypeScript会根据上下文自动推断出变量的类型。

// 推断为number
let num = 123;

// 推断为string
let str = "hello";

// 推断为number[]
let arr = [1, 2, 3];

// 推断函数返回值为number
function add(a: number, b: number) {
  return a + b;
}

类型推断让TypeScript代码更简洁,不需要每个变量都写类型注解。但在一些复杂的情况下,TypeScript可能推断不出类型,或者推断的类型不符合预期,这时候就需要显式写类型注解了。

接口

接口(interface)是TypeScript中非常重要的概念,用来定义对象的形状,也就是对象应该有哪些属性和方法,以及它们的类型。

基本用法

interface Person {
  name: string;
  age: number;
  gender: string;
}

function printPerson(person: Person): void {
  console.log(`姓名:${person.name},年龄:${person.age},性别:${person.gender}`);
}

const zhangsan: Person = {
  name: "张三",
  age: 25,
  gender: "男"
};

printPerson(zhangsan);

接口定义了对象必须有哪些属性,以及每个属性的类型。赋值给接口类型的对象,必须包含接口定义的所有属性,而且属性类型必须匹配。多了属性或者少了属性都会报错。

可选属性

接口的属性可以是可选的,在属性名后面加上?就行。可选属性可以存在,也可以不存在。

interface Person {
  name: string;
  age: number;
  email?: string; // 可选属性
  phone?: string; // 可选属性
}

const zhangsan: Person = {
  name: "张三",
  age: 25
  // email和phone可以不写
};

const lisi: Person = {
  name: "李四",
  age: 30,
  email: "lisi@example.com"
};

只读属性

接口的属性可以设置为只读,在属性名前面加上readonly就行。只读属性只能在对象创建的时候赋值,之后不能修改。

interface Point {
  readonly x: number;
  readonly y: number;
}

const p: Point = { x: 10, y: 20 };
// p.x = 30; // 报错,x是只读的

函数类型接口

接口也可以用来定义函数的类型。

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

const mySearch: SearchFunc = function(source: string, subString: string): boolean {
  return source.search(subString) !== -1;
};

可索引类型

接口还可以定义可索引类型,也就是对象可以通过索引访问,比如数组和字典。

// 数组类型的接口
interface StringArray {
  [index: number]: string;
}

const arr: StringArray = ["张三", "李四", "王五"];

// 字典类型的接口
interface NumberDictionary {
  [key: string]: number;
}

const dict: NumberDictionary = {
  apple: 5,
  banana: 3,
  orange: 8
};

接口继承

接口可以继承其他接口,用extends关键字。一个接口可以继承多个接口。

interface Animal {
  name: string;
  eat(): void;
}

interface Dog extends Animal {
  bark(): void;
}

const myDog: Dog = {
  name: "旺财",
  eat() {
    console.log("狗在吃东西");
  },
  bark() {
    console.log("汪汪汪");
  }
};

继承接口会拥有被继承接口的所有属性和方法,并且可以添加自己的属性和方法。

接口是TypeScript中定义对象形状的主要方式,在实际开发中使用非常广泛。掌握接口,是学好TypeScript的关键。

TypeScript对JavaScript的类进行了扩展,增加了类型注解、访问修饰符、抽象类、接口实现等特性。

基本用法

class Person {
  name: string;
  age: number;

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

  sayHello(): void {
    console.log(`大家好,我是${this.name},今年${this.age}岁`);
  }
}

const zhangsan = new Person("张三", 25);
zhangsan.sayHello();

访问修饰符

TypeScript支持三种访问修饰符:public、private和protected。

  • public:公开的,可以在任何地方访问,默认是public。
  • private:私有的,只能在类的内部访问,外部不能访问。
  • protected:受保护的,只能在类的内部和子类中访问。
class Person {
  public name: string;
  private age: number;
  protected gender: string;

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

  public sayHello(): void {
    console.log(`大家好,我是${this.name}`);
  }

  private getAge(): number {
    return this.age;
  }

  protected getGender(): string {
    return this.gender;
  }
}

const zhangsan = new Person("张三", 25, "男");
console.log(zhangsan.name); // 可以访问
// console.log(zhangsan.age); // 报错,age是private
// console.log(zhangsan.gender); // 报错,gender是protected

继承

类可以继承其他类,用extends关键字。子类会拥有父类的所有属性和方法,并且可以重写父类的方法。

class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  eat(): void {
    console.log(`${this.name}在吃东西`);
  }
}

class Dog extends Animal {
  bark(): void {
    console.log(`${this.name}在汪汪叫`);
  }

  // 重写父类的方法
  eat(): void {
    console.log(`${this.name}在吃骨头`);
  }
}

const dog = new Dog("旺财");
dog.eat(); // 旺财在吃骨头
dog.bark(); // 旺财在汪汪叫

子类的构造函数必须调用super()来执行父类的构造函数。

class Dog extends Animal {
  breed: string;

  constructor(name: string, breed: string) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }
}

抽象类

抽象类是不能被实例化的类,只能被继承。抽象类中可以包含抽象方法,抽象方法只有声明没有实现,必须在子类中实现。

abstract class Shape {
  abstract area(): number;

  printArea(): void {
    console.log(`面积是:${this.area()}`);
  }
}

class Circle extends Shape {
  radius: number;

  constructor(radius: number) {
    super();
    this.radius = radius;
  }

  area(): number {
    return Math.PI * this.radius * this.radius;
  }
}

const circle = new Circle(5);
circle.printArea();

类实现接口

类可以实现接口,用implements关键字。实现接口的类必须实现接口中定义的所有属性和方法。

interface Flyable {
  fly(): void;
}

interface Swimmable {
  swim(): void;
}

class Duck implements Flyable, Swimmable {
  fly(): void {
    console.log("鸭子在飞");
  }

  swim(): void {
    console.log("鸭子在游泳");
  }
}

类是面向对象编程的核心,TypeScript对类的增强让JavaScript的面向对象编程更强大、更规范。

泛型

泛型(Generics)是TypeScript中非常重要的特性,它允许我们在定义函数、接口、类的时候不预先指定具体的类型,而是在使用的时候再指定类型。这样可以提高代码的复用性和类型安全。

泛型函数

function identity<T>(arg: T): T {
  return arg;
}

// 使用的时候指定类型
const num = identity<number>(123);
const str = identity<string>("hello");

// 也可以让TypeScript自动推断类型
const num2 = identity(456); // 推断为number
const str2 = identity("world"); // 推断为string

这个identity函数可以接受任何类型的参数,并且返回相同类型的值。如果不用泛型,要么用any(失去类型安全),要么为每种类型写一个函数(代码重复)。

泛型接口

interface Box<T> {
  value: T;
  getValue(): T;
  setValue(value: T): void;
}

class NumberBox implements Box<number> {
  value: number;

  constructor(value: number) {
    this.value = value;
  }

  getValue(): number {
    return this.value;
  }

  setValue(value: number): void {
    this.value = value;
  }
}

泛型类

class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }

  isEmpty(): boolean {
    return this.items.length === 0;
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2

const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
console.log(stringStack.pop()); // world

泛型约束

泛型可以使用extends关键字进行约束,限制泛型的类型范围。

interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

logLength("hello"); // 5,字符串有length属性
logLength([1, 2, 3]); // 3,数组有length属性
// logLength(123); // 报错,数字没有length属性

这个例子中,泛型T被约束为必须有length属性,这样在函数内部就可以安全地访问arg.length了。

泛型是TypeScript中比较难理解但非常重要的特性,掌握了泛型,你就能写出更通用、更安全、更复用的代码。

高级类型

除了基础类型,TypeScript还有很多高级类型,让类型系统更强大、更灵活。

联合类型

联合类型用|表示,表示变量可以是多种类型中的一种。

let value: string | number;
value = "hello";
value = 123;
// value = true; // 报错,布尔类型不在联合类型中

使用联合类型的变量时,只能访问所有类型共有的属性和方法。如果要访问某个类型特有的属性,需要先进行类型收窄。

function printLength(value: string | number): void {
  if (typeof value === "string") {
    console.log(value.length); // 字符串有length属性
  } else {
    console.log(value.toFixed(2)); // 数字有toFixed方法
  }
}

交叉类型

交叉类型用&表示,表示同时具有多种类型的特性。

interface Person {
  name: string;
  age: number;
}

interface Employee {
  company: string;
  salary: number;
}

type PersonEmployee = Person & Employee;

const zhangsan: PersonEmployee = {
  name: "张三",
  age: 25,
  company: "某公司",
  salary: 10000
};

交叉类型的变量必须同时具有所有类型的属性和方法。

类型别名

类型别名用type关键字给一个类型起一个新名字。

type ID = number | string;
type Point = { x: number; y: number };
type Callback = (data: string) => void;

const id: ID = "abc123";
const point: Point = { x: 10, y: 20 };
const callback: Callback = (data) => console.log(data);

类型别名和接口有些类似,但也有区别。接口主要用于定义对象的形状,可以被继承和实现;类型别名更灵活,可以定义任何类型,包括联合类型、交叉类型、元组等。在实际开发中,定义对象形状优先用接口,其他情况用类型别名。

字面量类型

字面量类型表示变量只能是某个特定的字面量值。

type Direction = "up" | "down" | "left" | "right";
type Status = "pending" | "success" | "error";

let dir: Direction = "up";
// dir = "north"; // 报错,不在字面量类型中

let status: Status = "pending";
status = "success";

字面量类型通常和联合类型一起使用,用来限制变量的取值范围,比string类型更安全。

类型收窄

类型收窄是指通过条件判断,把联合类型的变量收窄为更具体的类型。常见的类型收窄方式有:

typeof收窄:

function process(value: string | number) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());
  } else {
    console.log(value.toFixed(2));
  }
}

instanceof收窄:

class Dog {
  bark() {
    console.log("汪汪汪");
  }
}

class Cat {
  meow() {
    console.log("喵喵喵");
  }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.meow();
  }
}

in收窄:

interface Circle {
  radius: number;
}

interface Square {
  side: number;
}

function area(shape: Circle | Square) {
  if ("radius" in shape) {
    return Math.PI * shape.radius * shape.radius;
  } else {
    return shape.side * shape.side;
  }
}

类型收窄是TypeScript中非常重要的概念,它让我们在使用联合类型的时候能够安全地访问特定类型的属性和方法。

实用类型

TypeScript内置了很多实用类型(Utility Types),用来进行类型转换。常见的有:

Partial<T>:把T的所有属性变为可选。

interface Person {
  name: string;
  age: number;
}

type PartialPerson = Partial<Person>;
// 等价于 { name?: string; age?: number }

Required<T>:把T的所有属性变为必选。

Readonly<T>:把T的所有属性变为只读。

Pick<T, K>:从T中选取部分属性K。

interface Person {
  name: string;
  age: number;
  gender: string;
}

type PersonNameAge = Pick<Person, "name" | "age">;
// 等价于 { name: string; age: number }

Omit<T, K>:从T中排除部分属性K。

Record<K, T>:构造一个对象类型,键的类型是K,值的类型是T。

type PersonMap = Record<string, Person>;
// 等价于 { [key: string]: Person }

ReturnType<T>:获取函数类型T的返回值类型。

function add(a: number, b: number): number {
  return a + b;
}

type AddReturnType = ReturnType<typeof add>;
// 等价于 number

这些实用类型在实际开发中非常有用,能让类型操作更简洁、更高效。TypeScript 5.4还新增了一些实用类型和改进,比如NoInfer等,可以关注官方文档了解详情。

模块

TypeScript支持ES6的模块系统,可以使用import和export来导入和导出模块。

导出

// utils.ts
export function add(a: number, b: number): number {
  return a + b;
}

export function subtract(a: number, b: number): number {
  return a - b;
}

export const PI = 3.14159;

export interface Point {
  x: number;
  y: number;
}

也可以使用默认导出:

// Calculator.ts
export default class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

导入

// main.ts
import { add, subtract, PI, Point } from "./utils";
import Calculator from "./Calculator";

console.log(add(1, 2)); // 3
console.log(subtract(5, 3)); // 2
console.log(PI); // 3.14159

const calc = new Calculator();
console.log(calc.add(10, 20)); // 30

也可以使用命名空间导入:

import * as utils from "./utils";
console.log(utils.add(1, 2));

TypeScript的模块系统和ES6一致,编译的时候可以配置输出的模块格式(commonjs、amd、es2020等),根据运行环境选择合适的模块格式。

TypeScript 5.4的新特性

最后简单介绍一下TypeScript 5.4的一些新特性,让你了解最新版本的改进。

第一,更智能的类型收窄。TypeScript 5.4改进了类型收窄算法,特别是在闭包和条件语句中的类型收窄更准确了。比如在if语句中收窄的类型,在闭包中也能正确保留,不会丢失类型信息。这让很多以前需要类型断言的场景现在可以自动推断了。

第二,NoInfer实用类型。TypeScript 5.4新增了NoInfer<T>实用类型,用来阻止TypeScript在泛型推断时使用某个参数的类型。这在一些复杂的泛型场景中很有用,可以避免类型推断过于宽泛或者不准确。

第三,更好的推断性能。TypeScript 5.4对类型推断的性能做了优化,特别是对于大型项目,编译速度有了明显提升。类型检查的缓存机制也做了改进,增量编译更快了。

第四,Object.groupBy和Map.groupBy的类型支持。TypeScript 5.4增加了对JavaScript新的Object.groupBy和Map.groupBy方法的类型支持,这些方法可以方便地对数组进行分组,TypeScript能正确推断分组后的类型。

第五,更严格的检查。TypeScript 5.4增加了一些新的严格检查,比如对某些常见错误模式的检测,能在编译时发现更多潜在的bug。

这些新特性让TypeScript 5.4比之前的版本更智能、更快、更安全。如果你还在使用旧版本的TypeScript,建议升级到5.4,体验这些新特性。

学习资源和建议

最后给一些学习TypeScript的资源和建议。

第一,官方文档。TypeScript的官方文档(https://www.typescriptlang.org/docs/)是最好的学习资源,内容全面、准确、更新及时。特别是Handbook部分,循序渐进地介绍了TypeScript的所有特性,非常适合初学者。

第二,动手实践。学习TypeScript不能只看书看教程,一定要动手写代码。可以从简单的小项目开始,比如用TypeScript写一个命令行工具、一个简单的Web应用,在实践中理解和掌握TypeScript的特性。也可以把现有的JavaScript项目逐步迁移到TypeScript,在迁移过程中学习。

第三,使用strict模式。学习TypeScript的时候,一定要开启strict模式,严格的类型检查能让你更快地理解TypeScript的类型系统,写出更规范的代码。不要为了省事关闭严格检查,那样你学不到TypeScript的精髓。

第四,善用IDE。使用支持TypeScript的IDE(比如VS Code),利用IDE的代码提示、类型检查、自动重构等功能,能大大提高学习效率和开发体验。遇到类型错误的时候,仔细看错误信息,理解错误原因,不要随便用any绕过。

第五,阅读优秀的TypeScript代码。学习TypeScript的一个好方法是阅读优秀的开源项目的TypeScript代码,看看别人是怎么写类型定义、怎么组织代码、怎么使用高级特性的。比如React、Vue、Angular等框架的源码,或者一些知名的TypeScript库,都值得一读。

第六,持续学习。TypeScript还在不断发展,新版本会不断带来新特性和改进。要持续关注TypeScript的更新,学习新特性,保持自己的知识更新。

写在最后

TypeScript 5.4+入门指南,到这里就结束了。

这篇文章从最基础的环境搭建开始,介绍了TypeScript的基础类型、接口、类、泛型、高级类型、模块等核心知识点,还简单介绍了TypeScript 5.4的新特性。希望通过这篇文章,能帮助你对TypeScript有一个全面的了解,能够用TypeScript编写简单的项目。

当然,TypeScript的内容远不止这些,这篇文章只是一个入门指南,还有很多高级特性(比如装饰器、命名空间、声明文件、类型体操等)没有深入介绍。但掌握了这些基础知识,你已经可以开始用TypeScript开发项目了,剩下的可以在实践中逐步学习和深入。

TypeScript已经成为了前端开发的标配,掌握TypeScript不仅能让你写出更安全、更可维护的代码,还能让你在找工作的时候更有竞争力。如果你还没有学TypeScript,现在就是最好的时机。

学习TypeScript的过程可能会有些痛苦,特别是对于习惯了JavaScript动态类型的开发者来说,需要转变思维方式,适应静态类型的约束。但只要坚持下去,你会发现TypeScript带来的好处远远大于这些痛苦,你会爱上这种有类型保障的开发体验。

希望这篇文章能成为你TypeScript学习之路的起点。祝你学习愉快,早日掌握TypeScript!

有什么问题或者建议,欢迎在评论区交流。