TypeScript 详细教程

48次阅读
没有评论

共计 13295 个字符,预计需要花费 34 分钟才能阅读完成。

目录

  1. TypeScript 简介
  2. 环境搭建
  3. 基础类型
  4. 接口
  5. 函数
  6. 泛型
  7. 模块系统
  8. 类型进阶
  9. 配置与工程化

TypeScript 简介

TypeScript 是 JavaScript 的超集,添加了静态类型系统。它由微软开发,可以在任何支持 JavaScript 的地方运行。

主要特性

  • 静态类型检查:在编译时发现错误
  • 类型推断:自动推断变量类型
  • 接口和泛型:更好的代码组织和复用
  • ES6+ 特性支持:支持最新的 JavaScript 特性
  • 工具支持:更好的 IDE 支持和代码补全

环境搭建

安装 TypeScript

# 全局安装
npm install -g typescript

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

验证安装

tsc --version

创建第一个 TypeScript 文件

创建 hello.ts

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

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

编译 TypeScript

# 编译单个文件
tsc hello.ts

# 使用配置文件编译
tsc

tsconfig.json 配置

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

基础类型

基本类型

// 布尔值
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 list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // 泛型语法

// 元组
let tuple: [string, number];
tuple = ["hello", 10]; // OK
// tuple = [10, "hello"]; // Error

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

enum ColorWithValues {
  Red = 1,
  Green = 2,
  Blue = 4
}

// Any
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false;

// 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
// create(42); // Error

// 类型断言
let someValue: any = "this is a string";
let strLength1: number = (someValue as string).length;
let strLength2: number = (<string>someValue).length;

联合类型和类型别名

// 联合类型
let union: string | number;
union = "hello";
union = 42;
// union = true; // Error

// 类型别名
type StringOrNumber = string | number;
let value: StringOrNumber;
value = "hello";
value = 123;

// 字面量类型
type Direction = "left" | "right" | "up" | "down";
let move: Direction = "left";
// move = "diagonal"; // Error

// 交叉类型
interface BusinessPartner {
  name: string;
  credit: number;
}

interface Identity {
  id: number;
  name: string;
}

type Employee = Identity & BusinessPartner;

let employee: Employee = {
  id: 1,
  name: "John",
  credit: 1000
};

接口

基础接口

// 基础接口
interface Person {
  readonly id: number; // 只读属性
  name: string;
  age?: number; // 可选属性
}

function printPerson(person: Person): void {console.log(`ID: ${person.id}, Name: ${person.name}`);
  if (person.age) {console.log(`Age: ${person.age}`);
  }
}

let john: Person = {
  id: 1,
  name: "John"
};

// 函数类型接口
interface SearchFunc {(source: string, subString: string): boolean;
}

let mySearch: SearchFunc = function(src: string, sub: string): boolean {return src.search(sub) > -1;
};

// 可索引类型接口
interface StringArray {[index: number]: string;
}

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

接口继承和实现

// 接口继承
interface Shape {color: string;}

interface Square extends Shape {sideLength: number;}

let square = {} as Square;
square.color = "blue";
square.sideLength = 10;

// 多继承
interface PenStroke {penWidth: number;}

interface SquareWithPen extends Shape, PenStroke {sideLength: number;}

// 类实现接口
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) {}}

基础类

class Animal {
  // 属性
  private name: string; // 私有属性,只能在类内部访问
  protected age: number; // 受保护属性,可以在子类中访问
  public readonly species: string; // 公开只读属性

  // 静态属性
  static numberOfAnimals: number = 0;

  // 构造函数
  constructor(name: string, age: number, species: string) {
    this.name = name;
    this.age = age;
    this.species = species;
    Animal.numberOfAnimals++;
  }

  // 方法
  public move(distanceInMeters: number = 0): void {console.log(`${this.name} moved ${distanceInMeters}m.`);
  }

  // 获取私有属性
  public getName(): string {return this.name;}

  // 静态方法
  static getNumberOfAnimals(): number {return Animal.numberOfAnimals;}
}

继承和多态

// 继承
class Dog extends Animal {
  private breed: string;

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

  // 方法重写
  public move(distanceInMeters: number = 5): void {console.log("Running...");
    super.move(distanceInMeters); // 调用父类方法
  }

  // 新方法
  public bark(): void {console.log("Woof! Woof!");
  }

  public getBreed(): string {return this.breed;}
}

// 使用
const dog = new Dog("Buddy", 3, "Golden Retriever");
dog.move(10);
dog.bark();
console.log(Animal.getNumberOfAnimals());

抽象类

abstract class Department {constructor(public name: string) {}

  // 抽象方法,必须在子类中实现
  abstract printMeeting(): void;

  // 具体方法
  printName(): void {console.log("Department name:" + this.name);
  }
}

class AccountingDepartment extends Department {constructor() {super("Accounting and Auditing");
  }

  printMeeting(): void {console.log("The Accounting Department meets each Monday at 10am.");
  }

  generateReports(): void {console.log("Generating accounting reports...");
  }
}

// 使用
let department: Department; // 允许创建对抽象类型的引用
// department = new Department(); // 错误:不能创建抽象类的实例
department = new AccountingDepartment(); // 允许对抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
// department.generateReports(); // 错误:方法在声明的抽象类中不存在

访问器

class Employee {
  private _fullName: string = "";

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

  set fullName(newName: string) {if (newName && newName.length > 3) {this._fullName = newName;} else {console.log("Error: Name must be longer than 3 characters");
    }
  }
}

let employee = new Employee();
employee.fullName = "John Doe";
console.log(employee.fullName);

函数

函数类型

// 函数声明
function add(x: number, y: number): number {return x + y;}

// 函数表达式
let myAdd = function(x: number, y: number): number {return x + y;};

// 箭头函数
let myAddArrow = (x: number, y: number): number => x + y;

// 可选参数和默认参数
function buildName(firstName: string, lastName?: string, middleName: string = ""): string {if (lastName) {return `${firstName} ${middleName} ${lastName}`;
  }
  return firstName;
}

// 剩余参数
function buildNameRest(firstName: string, ...restOfName: string[]): string {return firstName + "" + restOfName.join(" ");
}

// 函数重载
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {if (d !== undefined && y !== undefined) {return new Date(y, mOrTimestamp, d);
  } else {return new Date(mOrTimestamp);
  }
}

const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 2020);
// const d3 = makeDate(1, 3); // Error: 没有需要 2 参数的重载

this 和上下文

interface Card {
  suit: string;
  card: number;
}

interface Deck {suits: string[];
  cards: number[];
  createCardPicker(this: Deck): () => Card;}

let deck: Deck = {suits: ["hearts", "spades", "clubs", "diamonds"],
  cards: Array(52),
  createCardPicker: function(this: Deck) {return () => {let pickedCard = Math.floor(Math.random() * 52);
      let pickedSuit = Math.floor(pickedCard / 13);

      return {suit: this.suits[pickedSuit], card: pickedCard % 13};
    }
  }
}

let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
console.log("card:" + pickedCard.card + "of" + pickedCard.suit);

泛型

基础泛型

// 泛型函数
function identity<T>(arg: T): T {return arg;}

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

// 泛型接口
interface GenericIdentityFn<T> {(arg: T): T;
}

let myIdentity: GenericIdentityFn<number> = identity;

// 泛型类
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;

  constructor(zeroValue: T, add: (x: T, y: T) => T) {
    this.zeroValue = zeroValue;
    this.add = add;
  }
}

let myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y);

泛型约束

interface Lengthwise {length: number;}

function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length); // 现在我们知道 arg 有 length 属性
  return arg;
}

// loggingIdentity(3); // Error: number 没有 length 属性
loggingIdentity({length: 10, value: 3}); // OK

// 在泛型约束中使用类型参数
function getProperty<T, K extends keyof T>(obj: T, key: K) {return obj[key];
}

let x = {a: 1, b: 2, c: 3, d: 4};
getProperty(x, "a"); // OK
// getProperty(x, "m"); // Error: 参数类型 '"m"' 不能赋值给参数类型 '"a" | "b" | "c" | "d"'

泛型工具类型

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

// Partial - 所有属性变为可选
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {return { ...todo, ...fieldsToUpdate};
}

// Readonly - 所有属性变为只读
const todo: Readonly<Todo> = {
  title: "Delete inactive users",
  description: "...",
  completed: false
};

// todo.title = "Hello"; // Error: 无法分配到 "title",因为它是只读属性

// Pick - 从类型中选择一组属性
type TodoPreview = Pick<Todo, "title" | "completed">;
const preview: TodoPreview = {
  title: "Clean room",
  completed: false
};

// Omit - 从类型中排除一组属性
type TodoInfo = Omit<Todo, "completed">;
const info: TodoInfo = {
  title: "Pick up kids",
  description: "Kindergarten closes at 5pm"
};

模块系统

导出和导入

// math.ts - 导出
export const PI = 3.14;

export interface Circle {radius: number;}

export function calculateCircumference(diameter: number): number {return diameter * PI;}

// 默认导出
export default class Calculator {static add(x: number, y: number): number {return x + y;}
}

// app.ts - 导入
import Calculator, {PI, Circle, calculateCircumference} from './math';

const circle: Circle = {radius: 10};
console.log(calculateCircumference(circle.radius * 2));
console.log(Calculator.add(5, 3));

// 重新导出
export {PI as MathPI} from './math';

命名空间

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();

类型进阶

条件类型

type TypeName<T> =
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  T extends Function ? "function" :
  "object";

type T0 = TypeName<string>; // "string"
type T1 = TypeName<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"

// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;
type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]

// 排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;
type T5 = NonNullable<string | number | undefined>; // string | number

映射类型

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

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

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

// 选择特定属性
type PersonKeys = keyof Person; // "name" | "age" | "location"

// 自定义映射类型
type Optional<T> = {[P in keyof T]?: T[P];
};

type Nullable<T> = {[P in keyof T]: T[P] | null;
};

// 移除只读修饰符
type CreateMutable<T> = {-readonly [P in keyof T]: T[P];
};

// 移除可选修饰符
type Concrete<T> = {[P in keyof T]-?: T[P];
};

模板字面量类型

type World = "world";
type Greeting = `hello ${World}`; // "hello world"

type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";

type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"

// 字符串操作类型
type Getter<T extends string> = `get${Capitalize<T>}`;
type Setter<T extends string> = `set${Capitalize<T>}`;

type Name = "name";
type NameAccessors = Getter<Name> | Setter<Name>; // "getName" | "setName"

配置与工程化

完整 tsconfig.json

{
  "compilerOptions": {
    /* 基本选项 */
    "target": "ES2020", // 编译目标
    "module": "CommonJS", // 模块系统
    "lib": ["ES2020", "DOM"], // 包含的库文件
    "outDir": "./dist", // 输出目录
    "rootDir": "./src", // 源文件目录

    /* 严格类型检查选项 */
    "strict": true, // 启用所有严格类型检查选项
    "noImplicitAny": true, // 禁止隐式 any 类型
    "strictNullChecks": true, // 严格的 null 检查
    "strictFunctionTypes": true, // 严格的函数类型检查
    "strictBindCallApply": true, // 严格的 bind/call/apply 检查
    "strictPropertyInitialization": true, // 严格的属性初始化检查
    "noImplicitThis": true, // 禁止隐式的 this 类型
    "alwaysStrict": true, // 以严格模式解析并为每个源文件生成 "use strict"

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

    /* 模块解析选项 */
    "moduleResolution": "node", // 模块解析策略
    "baseUrl": "./", // 解析非相对模块名的基准目录
    "paths": { // 模块名到基于 baseUrl 的路径映射
      "@/*": ["src/*"],
      "@/components/*": ["src/components/*"]
    },
    "esModuleInterop": true, // 为导入 CommonJS 模块提供兼容性
    "allowSyntheticDefaultImports": true, // 允许从没有默认导出的模块进行默认导入

    /* Source Map 选项 */
    "sourceMap": true, // 生成相应的 .map 文件
    "declaration": true, // 生成相应的 .d.ts 文件
    "declarationMap": true, // 为声明文件生成 sourcemap

    /* 实验性选项 */
    "experimentalDecorators": true, // 启用装饰器
    "emitDecoratorMetadata": true, // 为装饰器提供元数据支持

    /* 高级选项 */
    "skipLibCheck": true, // 跳过所有声明文件的类型检查
    "forceConsistentCasingInFileNames": true // 禁止对同一文件使用不一致大小写的引用
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist",
    "**/*.test.ts"
  ]
}

与 React 集成

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

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

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

const UserList: React.FC<UserListProps> = ({users, onUserSelect}) => {const [selectedUserId, setSelectedUserId] = useState<number | null>(null);

  const handleUserClick = (user: User) => {setSelectedUserId(user.id);
    onUserSelect(user);
  };

  return (
    <div>
      <h2>User List</h2>
      <ul>
        {users.map(user => (
          <li 
            key={user.id}
            onClick={() => handleUserClick(user)}
            style={{ 
              cursor: 'pointer',
              backgroundColor: selectedUserId === user.id ? '#f0f0f0' : 'transparent'
            }}
          >
            {user.name} - {user.email}
          </li>
        ))}
      </ul>
    </div>
  );
};

// 自定义 Hook
function useUsers() {const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {const fetchUsers = async () => {
      try {setLoading(true);
        // 模拟 API 调用
        const response = await fetch('/api/users');
        const data = await response.json();
        setUsers(data);
      } catch (err) {setError(err.message);
      } finally {setLoading(false);
      }
    };

    fetchUsers();}, []);

  return {users, loading, error};
}

export default UserList;

最佳实践

  1. 启用严格模式:始终在 tsconfig.json 中设置 "strict": true
  2. 使用明确的类型:避免过度使用 any 类型
  3. 利用类型推断:在明显的地方让 TypeScript 推断类型
  4. 使用接口定义对象形状:而不是内联类型注解
  5. 利用泛型提高代码复用性
  6. 使用只读数组和元组:确保数据不可变性
  7. 使用 const 断言:用于字面量类型
// 好的实践
interface User {
  readonly id: number;
  name: string;
  email: string;
}

type UserList = ReadonlyArray<User>;

// const 断言
const positions = ["left", "right", "top", "bottom"] as const;
type Position = typeof positions[number]; // "left" | "right" | "top" | "bottom"

正文完
 0
一诺
版权声明:本站原创文章,由 一诺 于2025-10-15发表,共计13295字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码