TypeScript 4.9在2022年11月发布,带来了一些新特性。

本文是一篇TypeScript入门指南,从零开始讲解TypeScript的核心概念,包括类型系统、接口、泛型、类,以及4.9版本的新特性。

一、TypeScript是什么

1. 基本概念

TypeScript是JavaScript的超集。

  • 在JavaScript的基础上,增加了类型系统
  • 最终编译成JavaScript运行
  • 可以在开发阶段发现错误
  • 支持最新的JavaScript特性

简单说:TypeScript = JavaScript + 类型系统。

2. 为什么用TypeScript

用TypeScript的好处:

  • 类型安全:在编译阶段发现类型错误
  • 更好的IDE支持:智能提示、自动补全、重构
  • 代码更易维护:类型就是文档
  • 适合大型项目:多人协作更顺畅
  • 渐进式采用:可以和JavaScript混合使用

3. 安装和使用

安装TypeScript:

npm install -g typescript

编译:

tsc hello.ts

会生成hello.js文件。

也可以用ts-node直接运行:

npm install -g ts-node
ts-node hello.ts

二、基础类型

1. 基本类型

TypeScript的基本类型:

// 字符串
let name: string = "张三";

// 数字
let age: number = 25;

// 布尔值
let isStudent: boolean = true;

// 数组
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];

// 元组
let tuple: [string, number] = ["张三", 25];

// 枚举
enum Color { Red, Green, Blue }
let c: Color = Color.Green;

// Any
let anything: any = "可以是任何类型";

// Void
function sayHello(): void {
  console.log("Hello");
}

// Null和Undefined
let u: undefined = undefined;
let n: null = null;

// Never
function error(message: string): never {
  throw new Error(message);
}

// Unknown
let value: unknown = "未知类型";

2. 类型推断

TypeScript有类型推断,不一定要显式声明类型:

let name = "张三";  // 推断为string
let age = 25;       // 推断为number

// 但建议在函数参数和返回值上显式声明类型
function add(a: number, b: number): number {
  return a + b;
}

3. 联合类型

一个变量可以是多种类型:

let id: number | string;
id = 123;    // OK
id = "abc";  // OK
id = true;   // 错误

使用联合类型时,需要类型收窄:

function printId(id: number | string) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id);
  }
}

三、接口

1. 定义接口

接口用来定义对象的形状:

interface Person {
  name: string;
  age: number;
  isStudent?: boolean;  // 可选属性
  readonly id: number;  // 只读属性
}

let person: Person = {
  name: "张三",
  age: 25,
  id: 1
};

2. 接口继承

接口可以继承:

interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

let dog: Dog = {
  name: "旺财",
  breed: "金毛"
};

3. 函数接口

接口也可以定义函数类型:

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

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

四、类

1. 基本类

class Animal {
  name: string;

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

  move(distance: number = 0) {
    console.log(`${this.name} moved ${distance}m`);
  }
}

let animal = new Animal("动物");
animal.move(10);

2. 继承

class Dog extends Animal {
  breed: string;

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

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

  move(distance: number = 5) {
    console.log("跑着...");
    super.move(distance);
  }
}

let dog = new Dog("旺财", "金毛");
dog.bark();
dog.move(20);

3. 访问修饰符

class Person {
  public name: string;      // 公开,默认
  private age: number;      // 私有,只能在类内部访问
  protected id: number;     // 受保护,类和子类可以访问

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

4. 抽象类

abstract class Animal {
  abstract makeSound(): void;

  move() {
    console.log("移动中...");
  }
}

class Cat extends Animal {
  makeSound() {
    console.log("喵喵喵");
  }
}

五、泛型

1. 基本泛型

泛型让函数和类可以支持多种类型:

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

let output = identity<string>("hello");
let output2 = identity(123);  // 类型推断

2. 泛型接口

interface GenericIdentityFn<T> {
  (arg: T): T;
}

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

let myIdentity: GenericIdentityFn<number> = identity;

3. 泛型类

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

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

4. 泛型约束

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);  // 现在可以访问length了
  return arg;
}

六、高级类型

1. 类型别名

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;

function getName(n: NameOrResolver): Name {
  if (typeof n === "string") {
    return n;
  } else {
    return n();
  }
}

2. 交叉类型

interface A {
  a: string;
}

interface B {
  b: number;
}

type C = A & B;

let c: C = {
  a: "hello",
  b: 123
};

3. 类型守卫

function isString(value: any): value is string {
  return typeof value === "string";
}

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

4. 映射类型

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

// 所有属性变为只读
type ReadonlyPerson = Readonly<Person>;

// 所有属性变为可选
type PartialPerson = Partial<Person>;

// 自定义映射
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P];
};

七、TypeScript 4.9新特性

1. satisfies操作符

这是4.9最重要的新特性。

satisfies可以验证类型,同时保留字面量类型:

// 之前:类型推断太宽泛
const palette = {
  red: [255, 0, 0],
  green: "#00ff00",
  blue: [0, 0, 255]
};
// palette.red 被推断为 number[] | string,没有精确类型

// 用satisfies:验证类型,保留字面量类型
const palette2 = {
  red: [255, 0, 0],
  green: "#00ff00",
  blue: [0, 0, 255]
} satisfies Record<string, string | number[]>;

// palette2.red 精确推断为 number[]
// palette2.green 精确推断为 string

2. 自动访问器(Auto-Accessors)

class Person {
  accessor name: string;

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

// 编译后会生成getter和setter

3. in操作符收窄

interface Dog {
  bark(): void;
}

interface Cat {
  meow(): void;
}

function speak(animal: Dog | Cat) {
  if ("bark" in animal) {
    animal.bark();  // 收窄为Dog
  } else {
    animal.meow();  // 收窄为Cat
  }
}

4.9优化了in操作符的类型收窄。

4. 其他改进

  • 更严格的检查
  • 更好的错误提示
  • 性能优化
  • 支持更多的JavaScript新特性

八、最佳实践

1. 开启严格模式

在tsconfig.json中开启严格模式:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

2. 尽量不用any

  • any会失去类型保护
  • 用unknown代替any
  • 用类型断言时要小心

3. 善用接口和类型别名

  • 接口适合定义对象形状
  • 类型别名更灵活
  • 根据场景选择

4. 善用泛型

  • 泛型让代码更通用
  • 但不要过度使用
  • 简单场景用具体类型

5. 善用工具类型

TypeScript内置了很多工具类型:

  • Partial<T>:所有属性可选
  • Required<T>:所有属性必选
  • Readonly<T>:所有属性只读
  • Pick<T, K>:选取部分属性
  • Omit<T, K>:排除部分属性
  • Record<K, T>:构造对象类型

九、学习建议

1. 从JavaScript过渡

  • 如果你已经会JavaScript,学TypeScript很快
  • 先在现有项目中逐步引入
  • 从类型注解开始
  • 慢慢学习高级特性

2. 多写多练

  • 理论学习之后,要多实践
  • 用TypeScript写小项目
  • 遇到问题查文档
  • 不断积累

3. 看官方文档

  • TypeScript官方文档很详细
  • Handbook是最好的入门教程
  • 定期看更新日志
  • 了解新特性

4. 用好工具

  • VS Code对TypeScript支持很好
  • 安装ESLint和Prettier
  • 配置tsconfig.json
  • 善用IDE的智能提示

十、写在最后

TypeScript 4.9,是一个值得学习的版本。

它的类型系统,能让你的代码更安全、更易维护。satisfies操作符、自动访问器等新特性,让TypeScript更强大。

2022年了,TypeScript已经成为前端开发的标配。不管是React、Vue还是Node.js,都在拥抱TypeScript。学习TypeScript,是前端工程师的必修课。

最后,用一句话总结:"TypeScript是JavaScript的超集,增加了类型系统。从零开始,先学基础类型,再学接口、类、泛型,最后学高级类型。多写多练,就能掌握。"

愿你学好TypeScript,写出更安全的代码。