TypeScript详细教程

目录

  1. [TypeScript 简介与安装](#TypeScript 简介与安装)
  2. 基础类型与类型注解
  3. 接口(Interface)
  4. 类(Class)
  5. 函数与函数类型
  6. 泛型(Generics)
  7. 高级类型
  8. 类型推断与类型守卫
  9. 装饰器(Decorators)
  10. 模块与命名空间
  11. 类型声明(.d.ts)与第三方库
  12. [TypeScript 编译器配置(tsconfig.json)](#TypeScript 编译器配置(tsconfig.json))
  13. [与 React/Vue 的结合](#与 React/Vue 的结合)
  14. 实战:构建一个待办事项应用
  15. 进阶主题与最佳实践

1. TypeScript 简介与安装

1.1 什么是 TypeScript?

TypeScript 是 JavaScript 的静态类型超集,它编译为纯 JavaScript。它提供了类型系统、接口、泛型、装饰器等特性,能够在开发阶段捕捉潜在错误,提升代码质量和可维护性。

1.2 安装 TypeScript

全局安装 TypeScript 编译器:

bash 复制代码
npm install -g typescript

检查版本:

bash 复制代码
tsc --version

1.3 第一个 TypeScript 程序

创建 hello.ts:

typescript 复制代码
function greet(name: string): string {
  return `Hello, ${name}!`;
}

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

编译并运行:

bash 复制代码
tsc hello.ts      # 生成 hello.js
node hello.js     # 输出: Hello, TypeScript!

2. 基础类型与类型注解

TypeScript 支持与 JavaScript 几乎相同的数据类型,并额外提供了枚举、元组等。

2.1 布尔值、数字、字符串

typescript 复制代码
let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";

2.2 数组

typescript 复制代码
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // 泛型写法

2.3 元组(Tuple)

元组允许表示一个已知元素数量和类型的数组。

typescript 复制代码
let x: [string, number];
x = ["hello", 10];   // OK
// x = [10, "hello"]; // Error

2.4 枚举(Enum)

typescript 复制代码
enum Color { Red, Green, Blue }
let c: Color = Color.Green;

// 手动赋值
enum Status { Success = 200, NotFound = 404 }

2.5 Any 与 Unknown

  • any:绕过类型检查,不推荐使用(除非迁移旧代码)。
  • unknown:类型安全的 any,需要类型守卫才能使用。
typescript 复制代码
let notSure: any = 4;
notSure = "maybe a string";

let value: unknown = "hello";
if (typeof value === "string") {
  console.log(value.toUpperCase());
}

2.6 Void、Null、Undefined

typescript 复制代码
function warnUser(): void {
  console.log("This is a warning");
}

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

2.7 Never

never 表示永远不会有返回值的函数(如抛出异常或无限循环)。

typescript 复制代码
function error(message: string): never {
  throw new Error(message);
}

2.8 类型断言

typescript 复制代码
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
// 或者尖括号语法(在 .tsx 中不可用)
let len: number = (<string>someValue).length;

3. 接口(Interface)

接口用来定义对象的结构,是 TypeScript 的核心设计原则之一。

3.1 基本接口

typescript 复制代码
interface Person {
  name: string;
  age: number;
}

function greet(person: Person): string {
  return `Hello, ${person.name}`;
}

3.2 可选属性与只读属性

typescript 复制代码
interface Config {
  readonly id: number;
  name: string;
  age?: number; // 可选
}

let config: Config = { id: 1, name: "Alice" };
// config.id = 2; // 错误,只读

3.3 函数类型接口

typescript 复制代码
interface SearchFunc {
  (source: string, subString: string): boolean;
}

let mySearch: SearchFunc = function(src, sub) {
  return src.indexOf(sub) !== -1;
};

3.4 可索引类型(Indexable Types)

typescript 复制代码
interface StringArray {
  [index: number]: string;
}

let arr: StringArray = ["a", "b"];

3.5 接口继承

typescript 复制代码
interface Shape {
  color: string;
}

interface Square extends Shape {
  sideLength: number;
}

let square: Square = { color: "red", sideLength: 10 };

4. 类(Class)

TypeScript 对 ES6 类进行了增强,增加了访问修饰符、抽象类等。

4.1 基本类

typescript 复制代码
class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  move(distance: number = 0): void {
    console.log(`${this.name} moved ${distance}m.`);
  }
}

4.2 访问修饰符

  • public:默认,任何地方可见。
  • private:只能在类内部访问。
  • protected:类内部和子类中可见。
typescript 复制代码
class Person {
  private ssn: string;
  protected age: number;
  public name: string;

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

4.3 只读属性(readonly)

typescript 复制代码
class Octopus {
  readonly name: string;
  constructor(name: string) {
    this.name = name;
  }
}

4.4 参数属性(Parameter Properties)

typescript 复制代码
class Animal {
  constructor(public name: string, private age: number) {}
  // 自动创建并初始化 name 和 age 属性
}

4.5 继承(extends)

typescript 复制代码
class Dog extends Animal {
  breed: string;
  constructor(name: string, age: number, breed: string) {
    super(name, age);
    this.breed = breed;
  }
  bark(): void {
    console.log(`${this.name} barks!`);
  }
}

4.6 抽象类(abstract)

typescript 复制代码
abstract class Department {
  constructor(public name: string) {}
  abstract printMeeting(): void; // 必须在派生类中实现
}

class AccountingDepartment extends Department {
  printMeeting(): void {
    console.log(`Meeting for ${this.name}`);
  }
}

5. 函数与函数类型

5.1 函数类型注解

typescript 复制代码
// 完整写法
let myAdd: (x: number, y: number) => number = function(x, y) {
  return x + y;
};

// 类型推断
let myAdd2 = (x: number, y: number): number => x + y;

5.2 可选参数与默认参数

typescript 复制代码
function buildName(firstName: string, lastName?: string): string {
  return lastName ? `${firstName} ${lastName}` : firstName;
}

function buildNameDefault(firstName: string, lastName = "Smith"): string {
  return `${firstName} ${lastName}`;
}

5.3 剩余参数(Rest Parameters)

typescript 复制代码
function sum(...numbers: number[]): number {
  return numbers.reduce((a, b) => a + b, 0);
}

5.4 函数重载(Overloads)

TypeScript 允许为同一个函数定义多个类型签名。

typescript 复制代码
function makeDate(timestamp: number): Date;
function makeDate(year: number, month: number, day: number): Date;
function makeDate(yearOrTimestamp: number, month?: number, day?: number): Date {
  if (month !== undefined && day !== undefined) {
    return new Date(yearOrTimestamp, month, day);
  } else {
    return new Date(yearOrTimestamp);
  }
}

6. 泛型(Generics)

泛型允许创建可重用的组件,能够处理多种类型而不丢失类型信息。

6.1 泛型函数

typescript 复制代码
function identity<T>(arg: T): T {
  return arg;
}

let output = identity<string>("myString"); // 显式指定
let output2 = identity("myString");        // 类型推断

6.2 泛型接口

typescript 复制代码
interface GenericIdentityFn<T> {
  (arg: T): T;
}

let myIdentity: GenericIdentityFn<number> = identity;

6.3 泛型类

typescript 复制代码
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGeneric = new GenericNumber<number>();
myGeneric.zeroValue = 0;
myGeneric.add = (x, y) => x + y;

6.4 泛型约束(Constraints)

使用 extends 限制泛型类型。

typescript 复制代码
interface Lengthwise {
  length: number;
}

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

logLength("hello"); // OK
logLength([1, 2, 3]); // OK
// logLength(123); // 错误,数字没有 length

6.5 在泛型中使用类型参数

typescript 复制代码
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

7. 高级类型

7.1 联合类型(Union Types)

typescript 复制代码
let value: string | number;
value = "hello";
value = 42;

7.2 交叉类型(Intersection Types)

将多个类型合并为一个。

typescript 复制代码
interface A { a: string }
interface B { b: number }
type C = A & B; // 同时拥有 a 和 b

7.3 类型别名(Type Aliases)

typescript 复制代码
type Name = string;
type Point = { x: number; y: number };

7.4 字面量类型(Literal Types)

typescript 复制代码
type Direction = "up" | "down" | "left" | "right";
let move: Direction = "up";

7.5 可辨识联合(Discriminated Unions)

typescript 复制代码
interface Circle {
  kind: "circle";
  radius: number;
}
interface Square {
  kind: "square";
  sideLength: number;
}
type Shape = Circle | Square;

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle": return Math.PI * shape.radius ** 2;
    case "square": return shape.sideLength ** 2;
  }
}

7.6 映射类型(Mapped Types)

typescript 复制代码
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Partial<T> = {
  [P in keyof T]?: T[P];
};

// 使用内置工具类型
interface Person { name: string; age: number; }
type ReadonlyPerson = Readonly<Person>;

7.7 条件类型(Conditional Types)

typescript 复制代码
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<number>;  // false

7.8 内置工具类型

  • Partial<T>:所有属性变为可选。
  • Required<T>:所有属性变为必选。
  • Readonly<T>:所有属性变为只读。
  • Pick<T, K>:选取部分属性。
  • Omit<T, K>:排除部分属性。
  • Record<K, T>:构造一个对象类型。
  • Exclude<T, U>:从 T 中排除可赋值给 U 的类型。
  • ReturnType<T>:获取函数返回类型。
typescript 复制代码
type User = { id: number; name: string; email: string };
type UserPreview = Pick<User, "id" | "name">;
type UserWithoutEmail = Omit<User, "email">;

8. 类型推断与类型守卫

8.1 类型推断

TypeScript 会自动推断变量类型:

typescript 复制代码
let x = 3; // x 被推断为 number

8.2 类型守卫(Type Guards)

类型守卫用于在运行时缩小类型范围。

typeof 类型守卫
typescript 复制代码
function printAll(strs: string | string[] | null) {
  if (typeof strs === "object") {
    // 这里 strs 可能是数组或 null
  }
}
instanceof 类型守卫
typescript 复制代码
class Bird { fly() {} }
class Fish { swim() {} }

function move(animal: Bird | Fish) {
  if (animal instanceof Bird) {
    animal.fly();
  } else {
    animal.swim();
  }
}
自定义类型守卫(is)
typescript 复制代码
function isFish(pet: Bird | Fish): pet is Fish {
  return (pet as Fish).swim !== undefined;
}
in 操作符
typescript 复制代码
if ("swim" in animal) {
  // animal 是 Fish
}

8.3 断言函数(Assertion Functions)

typescript 复制代码
function assertIsNumber(val: any): asserts val is number {
  if (typeof val !== "number") {
    throw new Error("Not a number");
  }
}

9. 装饰器(Decorators)

装饰器是一种特殊声明,可以附加到类、方法、属性或参数上。注意 :装饰器目前是实验性特性,需要在 tsconfig.json 中启用 "experimentalDecorators": true。

9.1 类装饰器

typescript 复制代码
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

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

9.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;
  }
}

9.3 属性装饰器

typescript 复制代码
function format(formatString: string) {
  return function (target: any, propertyKey: string) {
    let value = target[propertyKey];
    const getter = function () {
      return `${formatString} ${value}`;
    };
    Object.defineProperty(target, propertyKey, { get: getter });
  };
}

9.4 参数装饰器

typescript 复制代码
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
  // 记录参数为必需的逻辑
}

10. 模块与命名空间

10.1 模块(Module)

TypeScript 遵循 ES6 模块规范。

typescript 复制代码
// math.ts
export function add(x: number, y: number): number {
  return x + y;
}
export const PI = 3.14;

// app.ts
import { add, PI } from "./math.js";
console.log(add(1, 2), PI);

10.2 默认导出

typescript 复制代码
// utils.ts
export default function greet(name: string): string {
  return `Hello ${name}`;
}

// app.ts
import greet from "./utils.js";

10.3 命名空间(Namespace)

命名空间是 TypeScript 早期用于组织代码的方式,现在推荐使用模块。

typescript 复制代码
namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }
  const lettersRegexp = /^[A-Za-z]+$/;
  export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
      return lettersRegexp.test(s);
    }
  }
}

let validator = new Validation.LettersOnlyValidator();

11. 类型声明(.d.ts)与第三方库

11.1 类型声明文件

当使用 JavaScript 库时,需要提供类型声明。TypeScript 社区通过 DefinitelyTyped 提供大量库的类型定义。

安装类型定义(以 lodash 为例):

bash 复制代码
npm install --save-dev @types/lodash

11.2 编写自己的 .d.ts

typescript 复制代码
// myLib.d.ts
declare function myLib(name: string): string;
declare namespace myLib {
  let version: string;
}

11.3 全局类型声明

typescript 复制代码
// global.d.ts
declare const __APP_VERSION__: string;

12. TypeScript 编译器配置(tsconfig.json)

12.1 生成配置文件

bash 复制代码
tsc --init

12.2 常用配置项详解

json 复制代码
{
  "compilerOptions": {
    /* 基本选项 */
    "target": "ES2020",               // 编译目标
    "module": "commonjs",             // 模块系统
    "lib": ["ES2020", "DOM"],         // 包含的库文件
    "outDir": "./dist",               // 输出目录
    "rootDir": "./src",               // 源码目录
    "strict": true,                   // 启用所有严格类型检查
    "esModuleInterop": true,          // 允许 default 导入非模块
    "skipLibCheck": true,             // 跳过声明文件检查
    "forceConsistentCasingInFileNames": true,
    
    /* 额外检查 */
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    
    /* 装饰器 */
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

13. 与 React/Vue 的结合

13.1 TypeScript + React

使用 create-react-app 创建 TypeScript 项目:

bash 复制代码
npx create-react-app my-app --template typescript
函数组件示例
tsx 复制代码
// Greeting.tsx
interface GreetingProps {
  name: string;
  age?: number;
}

const Greeting: React.FC<GreetingProps> = ({ name, age }) => {
  return <div>Hello {name}, age {age}</div>;
};

export default Greeting;
Hooks 类型
tsx 复制代码
const [count, setCount] = useState<number>(0);
const inputRef = useRef<HTMLInputElement>(null);

13.2 TypeScript + Vue 3

使用 Vue CLI:

bash 复制代码
vue create my-app
# 选择 TypeScript
组件示例(<script setup>)
vue 复制代码
<script setup lang="ts">
interface Props {
  title: string;
  count?: number;
}
const props = defineProps<Props>();
const emit = defineEmits<{
  (e: 'update', value: number): void;
}>();
</script>

14. 实战:构建一个待办事项应用

我们将使用 TypeScript + 原生 DOM 操作,构建一个简单的 Todo 应用。

14.1 项目结构

复制代码
todo-ts/
├── src/
│   ├── models/
│   │   └── Todo.ts
│   ├── services/
│   │   └── TodoService.ts
│   ├── ui/
│   │   └── TodoUI.ts
│   └── index.ts
├── index.html
├── tsconfig.json
└── package.json

14.2 定义 Todo 模型(models/Todo.ts)

typescript 复制代码
export interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

14.3 服务层(services/TodoService.ts)

typescript 复制代码
import { Todo } from "../models/Todo.js";

export class TodoService {
  private todos: Todo[] = [];
  private nextId = 1;

  getTodos(): Todo[] {
    return this.todos;
  }

  addTodo(text: string): void {
    const newTodo: Todo = {
      id: this.nextId++,
      text,
      completed: false,
    };
    this.todos.push(newTodo);
  }

  toggleTodo(id: number): void {
    const todo = this.todos.find(t => t.id === id);
    if (todo) todo.completed = !todo.completed;
  }

  deleteTodo(id: number): void {
    this.todos = this.todos.filter(t => t.id !== id);
  }
}

14.4 UI 层(ui/TodoUI.ts)

typescript 复制代码
import { Todo } from "../models/Todo.js";
import { TodoService } from "../services/TodoService.js";

export class TodoUI {
  private todoListElement: HTMLUListElement;
  private inputElement: HTMLInputElement;

  constructor(private todoService: TodoService) {
    this.todoListElement = document.getElementById("todo-list") as HTMLUListElement;
    this.inputElement = document.getElementById("todo-input") as HTMLInputElement;
    this.bindEvents();
    this.render();
  }

  private bindEvents(): void {
    const addButton = document.getElementById("add-btn");
    addButton?.addEventListener("click", () => this.addTodo());
    this.inputElement.addEventListener("keypress", (e) => {
      if (e.key === "Enter") this.addTodo();
    });
  }

  private addTodo(): void {
    const text = this.inputElement.value.trim();
    if (text) {
      this.todoService.addTodo(text);
      this.inputElement.value = "";
      this.render();
    }
  }

  private toggleTodo(id: number): void {
    this.todoService.toggleTodo(id);
    this.render();
  }

  private deleteTodo(id: number): void {
    this.todoService.deleteTodo(id);
    this.render();
  }

  private render(): void {
    const todos = this.todoService.getTodos();
    this.todoListElement.innerHTML = "";
    todos.forEach(todo => {
      const li = document.createElement("li");
      li.className = todo.completed ? "completed" : "";
      li.innerHTML = `
        <span>${todo.text}</span>
        <button class="toggle-btn">✓</button>
        <button class="delete-btn">✗</button>
      `;
      const toggleBtn = li.querySelector(".toggle-btn") as HTMLButtonElement;
      const deleteBtn = li.querySelector(".delete-btn") as HTMLButtonElement;
      toggleBtn.addEventListener("click", () => this.toggleTodo(todo.id));
      deleteBtn.addEventListener("click", () => this.deleteTodo(todo.id));
      this.todoListElement.appendChild(li);
    });
  }
}

14.5 入口文件(index.ts)

typescript 复制代码
import { TodoService } from "./services/TodoService.js";
import { TodoUI } from "./ui/TodoUI.js";

const todoService = new TodoService();
const ui = new TodoUI(todoService);

14.6 HTML(index.html)

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <title>TypeScript Todo</title>
  <style>
    .completed span { text-decoration: line-through; opacity: 0.6; }
  </style>
</head>
<body>
  <div>
    <input type="text" id="todo-input" placeholder="Add a task..." />
    <button id="add-btn">Add</button>
    <ul id="todo-list"></ul>
  </div>
  <script type="module" src="./dist/index.js"></script>
</body>
</html>

14.7 编译与运行

配置 tsconfig.json 中 "module": "ESNext","outDir": "./dist"。

bash 复制代码
tsc
# 然后打开 index.html 或使用 live server

15. 进阶主题与最佳实践

15.1 声明合并(Declaration Merging)

TypeScript 会将多个同名的接口、命名空间等合并。

typescript 复制代码
interface Box {
  height: number;
}
interface Box {
  width: number;
}
// 最终 Box 拥有 height 和 width

15.2 符号(Symbols)

typescript 复制代码
const uniqueKey = Symbol("key");
class MyClass {
  [uniqueKey] = 123;
}

15.3 模板字面量类型(Template Literal Types)

typescript 复制代码
type EventName = `on${Capitalize<string>}`;
type OnClick = EventName; // "onClick" 等

15.4 实用类型模式

递归 Partial
typescript 复制代码
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
提取 Promise 内部类型
typescript 复制代码
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

15.5 性能与最佳实践

  • 避免滥用 any :使用 unknown 替代,配合类型守卫。
  • 启用严格模式 :strict: true 捕获更多错误。
  • 使用 readonly 防止意外修改。
  • 利用类型推断,但复杂类型显式注解。
  • 为函数和类编写 JSDoc 注释,配合类型系统。
  • 使用 eslint + @typescript-eslint 保持代码规范。

15.6 项目迁移策略

  1. 将 .js 重命名为 .ts。
  2. 设置 allowJs: true 和 checkJs: false 逐步迁移。
  3. 添加 // @ts-nocheck 到尚未修复的文件。
  4. 逐步修复类型错误,最终移除 any。
相关推荐
码艺-Alimjan20 分钟前
维吾尔语 RTL 排版的隐形利器:`\u200F`
前端·javascript
Highcharts.js1 小时前
Highcharts 数据过滤实战指南:从基础实现到性能优化
javascript·性能优化·bi·highcharts·前端可视化·数据过滤·交互图表
何何____2 小时前
TypeScript 函数类型兼容性
前端·javascript·typescript
雪芽蓝域zzs2 小时前
json数据转化成interface属性
前端·javascript·json
鬼手点金3 小时前
ReAct 循环
linux·服务器·前端·javascript·react.js·前端框架
雪芽蓝域zzs3 小时前
Vue导入方法三种方式对比
前端·javascript·vue.js
VimorphTech5 小时前
React 任务轮询,为什么不能只写一个 setInterval?
javascript·typescript
lexiangqicheng5 小时前
Docker的沙箱机制是怎样的?
前端·javascript
LCG米6 小时前
STM32 HardFault故障定位实测:压栈帧寄存器解析、CFSR故障源判读与出错地址回溯
前端·javascript·stm32·单片机·嵌入式