Skip to content

TypeScript深入解析:从基础到高级特性的全面指南 ​

TypeScript是Microsoft开发的开源编程语言,它是JavaScript的一个超集,添加了可选的静态类型和基于类的面向对象编程。TypeScript可以编译成纯JavaScript,并且可以在任何浏览器、Node.js或任何支持ECMAScript 3(或更高版本)的JavaScript引擎中运行。本文将详细介绍TypeScript的基础知识和高级特性。

1. TypeScript简介 ​

TypeScript由Anders Hejlsberg(C#之父)领导开发,于2012年首次发布。它旨在解决JavaScript在大型应用开发中的痛点,通过引入静态类型检查来提高代码质量和开发效率。

1.1 TypeScript的主要特性 ​

  • 静态类型检查:在编译时发现类型错误
  • 类型推断:自动推断变量类型
  • 接口和类:支持面向对象编程
  • 泛型:创建可重用的组件
  • 装饰器:为类声明和成员添加注解
  • 模块系统:支持ES6模块和CommonJS
  • 装饰器:为类声明和成员添加元编程能力

1.2 TypeScript与JavaScript的关系 ​

javascript
// JavaScript代码
function greet(person, date) {
  console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}

greet("Maddison", Date()); // 错误调用,Date()返回字符串而不是Date对象
typescript
// TypeScript代码
function greet(person: string, date: Date) {
  console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}

greet("Maddison", new Date()); // 正确调用
greet("Maddison", Date()); // 编译时报错

2. TypeScript环境搭建 ​

2.1 安装TypeScript ​

bash
# 全局安装TypeScript
npm install -g typescript

# 或者作为开发依赖安装到项目中
npm install --save-dev typescript

# 验证安装
tsc --version

2.2 初始化TypeScript项目 ​

bash
# 创建tsconfig.json配置文件
tsc --init

# 或者手动创建配置文件
{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

2.3 编译TypeScript代码 ​

bash
# 编译单个文件
tsc hello.ts

# 编译整个项目
tsc

# 监听模式编译
tsc --watch

# 使用配置文件编译
tsc --project tsconfig.json

3. TypeScript基础类型 ​

3.1 基本类型 ​

typescript
// 布尔值
let isDone: boolean = false;

// 数字
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

// 字符串
let color: string = "blue";
color = 'red';

// 模板字符串
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}. I'll be ${age + 1} years old next month.`;

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

// 元组 Tuple
let x: [string, number];
x = ['hello', 10]; // OK
// x = [10, 'hello']; // Error

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

// 枚举默认从0开始编号,也可以手动赋值
enum Color2 {Red = 1, Green, Blue}
let c2: Color2 = Color2.Green;

// Any类型
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

// Void类型
function warnUser(): void {
  console.log("This is my warning message");
}

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

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

// Object类型
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK

3.2 类型断言 ​

typescript
// "尖括号"语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

// as语法
let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;

4. 接口(Interfaces) ​

4.1 基本接口 ​

typescript
interface Person {
  firstName: string;
  lastName: string;
}

function greeter(person: Person) {
  return "Hello, " + person.firstName + " " + person.lastName;
}

let user = { firstName: "Jane", lastName: "User" };
console.log(greeter(user));

4.2 可选属性 ​

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"});

4.3 只读属性 ​

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

let p1: Point = { x: 10, y: 20 };
// p1.x = 5; // Error!

4.4 函数类型 ​

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

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

4.5 可索引类型 ​

typescript
interface StringArray {
  [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

4.6 类类型 ​

typescript
interface ClockInterface {
  currentTime: Date;
  setTime(d: Date): void;
}

class Clock implements ClockInterface {
  currentTime: Date = new Date();
  setTime(d: Date) {
    this.currentTime = d;
  }
  constructor(h: number, m: number) { }
}

5. 类(Classes) ​

5.1 基本类定义 ​

typescript
class Greeter {
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }

  greet() {
    return "Hello, " + this.greeting;
  }
}

let greeter = new Greeter("world");

5.2 继承 ​

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(10);
tom.move(34);

5.3 访问修饰符 ​

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

class Rhino extends Animal {
  constructor() { super("Rhino"); }
}

class Employee {
  private name: string;
  constructor(theName: string) { this.name = theName; }
}

let animal = new Animal("Goat");
let rhino = new Rhino();
let employee = new Employee("Bob");

animal = rhino;
// animal = employee; // Error: 'Animal' and 'Employee' are not compatible

5.4 Readonly修饰符 ​

typescript
class Octopus {
  readonly name: string;
  readonly numberOfLegs: number = 8;

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

let dad = new Octopus("Man with the 8 strong legs");
// dad.name = "Man with the 3-piece suit"; // Error! name is readonly

5.5 存取器 ​

typescript
class Employee {
  private _fullName: string = "";

  get fullName(): string {
    return this._fullName;
  }

  set fullName(newName: string) {
    if (newName && newName.length > 0) {
      this._fullName = newName;
    } else {
      console.log("Error: fullName cannot be empty");
    }
  }
}

let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
  console.log(employee.fullName);
}

6. 泛型(Generics) ​

6.1 基本泛型 ​

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

let output1 = identity<string>("myString");
let output2 = identity("myString"); // 类型推断

6.2 泛型接口 ​

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

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

let myIdentity: GenericIdentityFn<number> = identity;

6.3 泛型类 ​

typescript
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; };

6.4 泛型约束 ​

typescript
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // Now we know it has a .length property, so no more error
  return arg;
}

loggingIdentity(3);  // Error, number doesn't have a .length property
loggingIdentity({length: 10, value: 3}); // OK

7. 高级类型 ​

7.1 交叉类型 ​

typescript
interface Person {
  name: string;
}

interface Serializable {
  serialize(): string;
}

// 交叉类型
type LoggablePerson = Person & Serializable;

class Employee implements LoggablePerson {
  name: string;

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

  serialize(): string {
    return JSON.stringify(this);
  }
}

7.2 联合类型 ​

typescript
function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

7.3 类型保护 ​

typescript
// typeof类型保护
function isNumber(x: any): x is number {
  return typeof x === "number";
}

// instanceof类型保护
class Bird {
  fly() {}
  layEggs() {}
}

class Fish {
  swim() {}
  layEggs() {}
}

function getRandomPet(): Bird | Fish {
  return Math.random() > 0.5 ? new Bird() : new Fish();
}

let pet = getRandomPet();
if (pet instanceof Bird) {
  pet.fly();
} else if (pet instanceof Fish) {
  pet.swim();
}

7.4 可辨识联合 ​

typescript
interface Square {
  kind: "square";
  size: number;
}

interface Rectangle {
  kind: "rectangle";
  width: number;
  height: number;
}

interface Circle {
  kind: "circle";
  radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape) {
  switch (s.kind) {
    case "square": return s.size * s.size;
    case "rectangle": return s.height * s.width;
    case "circle": return Math.PI * s.radius ** 2;
  }
}

8. 装饰器(Decorators) ​

8.1 类装饰器 ​

typescript
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

@sealed
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    return "Hello, " + this.greeting;
  }
}

8.2 方法装饰器 ​

typescript
function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value;
  };
}

class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }

  @enumerable(false)
  greet() {
    return "Hello, " + this.greeting;
  }
}

8.3 属性装饰器 ​

typescript
function format(formatString: string) {
  return function (target: any, propertyKey: string) {
    // 属性装饰器逻辑
  };
}

class Greeter {
  @format("Hello, %s")
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }
}

9. 模块和命名空间 ​

9.1 ES6模块 ​

typescript
// math.ts
export function add(x: number, y: number): number {
  return x + y;
}

export function subtract(x: number, y: number): number {
  return x - y;
}

// main.ts
import { add, subtract } from './math';
console.log(add(1, 2)); // 3

9.2 命名空间 ​

typescript
namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }

  const lettersRegexp = /^[A-Za-z]+$/;
  const numberRegexp = /^[0-9]+$/;

  export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
      return lettersRegexp.test(s);
    }
  }

  export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
      return s.length === 5 && numberRegexp.test(s);
    }
  }
}

// 使用命名空间
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

10. TypeScript配置 ​

10.1 tsconfig.json配置详解 ​

json
{
  "compilerOptions": {
    /* 基本选项 */
    "target": "es2016",                          /* 指定 ECMAScript 目标版本 */
    "module": "commonjs",                        /* 指定生成哪个模块系统代码 */
    "lib": [],                                   /* 指定要包含在编译中的库文件 */
    "allowJs": true,                             /* 允许编译 javascript 文件 */
    "checkJs": true,                             /* 报告 javascript 文件中的错误 */
    "jsx": "preserve",                           /* 指定 jsx 代码用于的开发环境 */
    "declaration": true,                         /* 生成相应的 '.d.ts' 文件 */
    "sourceMap": true,                           /* 生成相应的 '.map' 文件 */
    "outFile": "./",                             /* 将输出文件合并为一个文件 */
    "outDir": "./dist",                          /* 指定输出目录 */
    "rootDir": "./src",                          /* 用来控制输出目录结构 */
    "removeComments": true,                      /* 删除编译后的所有的注释 */

    /* 严格的类型检查选项 */
    "strict": true,                              /* 启用所有严格类型检查选项 */
    "noImplicitAny": true,                       /* 在表达式和声明上有隐含的 any类型时报错 */
    "strictNullChecks": true,                    /* 启用严格的 null 检查 */
    "strictFunctionTypes": true,                 /* 启用严格的函数类型检查 */
    "strictBindCallApply": true,                 /* 启用严格的 bind, call, apply 检查 */
    "strictPropertyInitialization": true,        /* 启用类的属性初始化检查 */
    "noImplicitThis": true,                      /* 当 this 表达式值为 any 类型的时候,生成一个错误 */
    "alwaysStrict": true,                        /* 以严格模式检查每个模块,并在每个文件里加入 'use strict' */

    /* 额外的检查 */
    "noUnusedLocals": true,                      /* 有未使用的变量时,抛出错误 */
    "noUnusedParameters": true,                  /* 有未使用的参数时,抛出错误 */
    "noImplicitReturns": true,                   /* 并不是所有函数里的代码都有返回值时,抛出错误 */
    "noFallthroughCasesInSwitch": true,          /* 报告 switch 语句的 fallthrough 错误 */

    /* 模块解析选项 */
    "moduleResolution": "node",                  /* 选择模块解析策略 */
    "baseUrl": "./",                             /* 解析非相对模块名的基准目录 */
    "paths": {},                                 /* 模块名到基于 baseUrl 的路径映射的列表 */
    "rootDirs": [],                              /* 根文件夹列表 */
    "typeRoots": [],                             /* 包含类型定义的文件夹列表 */
    "types": [],                                 /* 需要包含的类型声明文件名列表 */
    "allowSyntheticDefaultImports": true,        /* 允许从没有设置默认导出的模块中默认导入 */
    "esModuleInterop": true,                     /* 通过为所有导入创建命名空间对象,实现 CommonJS 和 ES 模块之间的互操作性 */

    /* Source Map Options */
    "sourceRoot": "",                            /* 指定调试器应该找到 TypeScript 文件而不是源文件的位置 */
    "mapRoot": "",                               /* 指定调试器应该找到映射文件而不是生成文件的位置 */
    "inlineSourceMap": true,                     /* 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件 */
    "inlineSources": true,                       /* 将代码与 sourcemaps 生成到一个文件中 */

    /* 其他选项 */
    "experimentalDecorators": true,              /* 启用实验性的ES装饰器 */
    "emitDecoratorMetadata": true,               /* 给源码里的装饰器声明加上设计类型元数据 */
    "skipLibCheck": true,                        /* 跳过声明文件的类型检查 */
    "forceConsistentCasingInFileNames": true     /* 不允许不一致的引用同一文件的大小写 */
  }
}

11. TypeScript与现代前端框架 ​

11.1 TypeScript与React ​

typescript
// React组件示例
import React, { useState, useEffect } from 'react';

interface User {
  id: number;
  name: string;
  email: string;
}

interface UserListProps {
  users: User[];
  onSelectUser: (user: User) => void;
}

const UserList: React.FC<UserListProps> = ({ users, onSelectUser }) => {
  const [selectedUser, setSelectedUser] = useState<User | null>(null);

  useEffect(() => {
    if (selectedUser) {
      onSelectUser(selectedUser);
    }
  }, [selectedUser, onSelectUser]);

  return (
    <div>
      {users.map(user => (
        <div
          key={user.id}
          onClick={() => setSelectedUser(user)}
          className={selectedUser?.id === user.id ? 'selected' : ''}
        >
          <h3>{user.name}</h3>
          <p>{user.email}</p>
        </div>
      ))}
    </div>
  );
};

export default UserList;

11.2 TypeScript与Vue ​

typescript
// Vue 3 Composition API 示例
import { defineComponent, ref, computed, PropType } from 'vue';

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

export default defineComponent({
  name: 'TodoList',
  props: {
    todos: {
      type: Array as PropType<Todo[]>,
      required: true
    }
  },
  setup(props) {
    const newTodo = ref('');
    const filteredTodos = computed(() => {
      return props.todos.filter(todo => !todo.completed);
    });

    const addTodo = () => {
      if (newTodo.value.trim()) {
        // 发送事件给父组件
        emit('add-todo', newTodo.value);
        newTodo.value = '';
      }
    };

    return {
      newTodo,
      filteredTodos,
      addTodo
    };
  }
});

12. TypeScript最佳实践 ​

12.1 使用严格模式 ​

始终在tsconfig.json中启用严格模式:

json
{
  "compilerOptions": {
    "strict": true
  }
}

12.2 合理使用any类型 ​

避免过度使用any类型,尽量使用具体的类型或泛型。

12.3 使用接口而非类型别名定义对象形状 ​

typescript
// 推荐
interface User {
  name: string;
  age: number;
}

// 而不是
type User = {
  name: string;
  age: number;
};

12.4 使用联合类型而非枚举 ​

typescript
// 推荐
type Direction = 'north' | 'south' | 'east' | 'west';

// 而不是
enum Direction {
  North,
  South,
  East,
  West
}

12.5 使用类型守卫 ​

typescript
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

// 使用
if (isString(input)) {
  // 在这里input被推断为string类型
  console.log(input.toUpperCase());
}

13. TypeScript工具和生态系统 ​

13.1 TypeScript Playground ​

TypeScript官方提供的在线编辑器,可以快速测试TypeScript代码: https://www.typescriptlang.org/play

13.2 DefinitelyTyped ​

社区维护的类型定义库,为流行的JavaScript库提供类型定义:

bash
npm install --save-dev @types/node
npm install --save-dev @types/lodash

13.3 TypeScript ESLint ​

结合ESLint进行代码质量检查:

bash
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin

总结 ​

TypeScript通过引入静态类型检查,极大地提高了JavaScript代码的质量和可维护性。通过本文的学习,你应该掌握了:

  1. TypeScript的基本类型和类型系统
  2. 接口和类的使用
  3. 泛型的概念和应用
  4. 高级类型特性如联合类型、交叉类型等
  5. 装饰器的使用
  6. 模块系统和命名空间
  7. TypeScript配置选项
  8. 与现代前端框架的集成
  9. 最佳实践和工具生态

在实际项目中,合理使用TypeScript可以显著减少运行时错误,提高代码可读性和团队协作效率。随着TypeScript生态的不断完善,它已成为现代前端开发的重要组成部分。