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: truecheckJs: false 逐步迁移。
  3. 添加 // @ts-nocheck 到尚未修复的文件。
  4. 逐步修复类型错误,最终移除 any
相关推荐
haierccc3 小时前
Win7、2008R2、Win10、Win11使用FLASH的方法
前端·javascript·html
We་ct3 小时前
LeetCode 50. Pow(x, n):从暴力法到快速幂的优化之路
开发语言·前端·javascript·算法·leetcode·typescript·
Hilaku3 小时前
卷AI、卷算法、2026 年的前端工程师到底在卷什么?
前端·javascript·面试
comedate3 小时前
[TypeScript] TypeScript 学习从入门到精通
ubuntu·typescript·前端语言
军军君013 小时前
Three.js基础功能学习十五:智能黑板实现实例二
开发语言·前端·javascript·vue.js·3d·threejs·三维
四千岁4 小时前
Ollama+OpenWebUI 最佳组合:本地大模型可视化交互方案
前端·javascript·后端
写不来代码的草莓熊4 小时前
el-date-picker ,自定义输入数字自动转换显示yyyy-mm-dd HH:mm:ss格式
前端·javascript·vue.js
Wect4 小时前
LeetCode 149. 直线上最多的点数:题解深度剖析
前端·算法·typescript
Wect4 小时前
JS手撕:手写Koa中间件与Promise核心特性
前端·javascript·面试