核心知识点
1. TypeScript 简介
1.1 什么是 TypeScript?
TypeScript 是 JavaScript 的一个超集,它向 JavaScript 添加了静态类型系统。这意味着你可以在代码编写阶段就定义变量、函数参数和返回值的类型,并在编译时进行检查。TypeScript 代码最终会被编译成纯 JavaScript 代码,可以在任何支持 JavaScript 的环境中运行。
1.2 为什么使用 TypeScript?
- 静态类型检查: 在编译阶段就能发现许多常见的错误(如拼写错误、类型不匹配),减少运行时 Bug。
 - 代码可读性和可维护性: 类型注解使得代码意图更清晰,便于团队协作和长期维护。
 - 更好的开发工具支持: 类型信息能让 IDE(如 VS Code)提供更智能的代码补全、重构和错误提示。
 - 访问最新 JavaScript 特性: TypeScript 允许你使用 ECMAScript 的最新特性,并可以编译到旧版本的 JavaScript 以兼容旧环境。
 
2. 基础类型 (Basic Types)
TypeScript 支持与 JavaScript 几乎相同的基本数据类型,并提供了明确的类型注解。
2.1 boolean
表示布尔值 true 或 false。
let isDone: boolean = false;
// isDone = 'hello'; // Error: Type 'string' is not assignable to type 'boolean'.
2.2 number
表示所有数字,包括整数和浮点数。
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let score: number = 99.5;
2.3 string
表示文本数据,可以使用单引号 (')、双引号 (") 或模板字符串 (`)。
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.`;
2.4 array
表示数组,有两种定义方式:
- 元素类型后跟 
[] - 使用数组泛型 
Array<元素类型> 
// 方式一
let list1: number[] = [1, 2, 3];
// list1.push('4'); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
// 方式二
let list2: Array<string> = ["a", "b", "c"];
2.5 tuple (元组)
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let x: [string, number];
x = ["hello", 10]; // OK
// x = [10, 'hello']; // Error: Type 'number' is not assignable to type 'string'. Type 'string' is not assignable to type 'number'.
console.log(x[0].substring(1)); // OK
// console.log(x[1].substring(1)); // Error: Property 'substring' does not exist on type 'number'.
// x[2] = 'world'; // Error: Tuple type '[string, number]' of length '2' has no element at index '2'. (Length is fixed unless configured differently)
2.6 enum (枚举)
枚举类型是对 JavaScript 标准数据类型的一个补充,它允许你为一组数值赋予友好的名字。
enum Color {
  Red, // 默认从 0 开始
  Green, // 1
  Blue, // 2
}
let c: Color = Color.Green;
console.log(c); // 输出 1
enum Status {
  Pending = 1,
  Processing, // 自动递增为 2
  Completed, // 自动递增为 3
}
let currentStatus: Status = Status.Processing;
console.log(currentStatus); // 输出 2
// 也可以通过值获取名字
let colorName: string = Color[2];
console.log(colorName); // 输出 'Blue'
2.7 any
any 类型允许你绕过编译时的类型检查。当你不确定某个值的类型时,或者它来自于动态内容(如用户输入或第三方库),可以使用 any。应尽量避免使用 any,因为它会失去 TypeScript 的类型保护优势。
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
notSure.toFixed(); // Okay, toFixed exists (but may fail at runtime if notSure is not a number)
notSure.nonExistentMethod(); // Okay at compile time, will fail at runtime
2.8 unknown
unknown 是 any 的类型安全对应物。任何类型的值都可以赋给 unknown,但你不能对 unknown 类型的值执行任何操作,除非你先进行了类型检查或类型断言,将其缩小(narrow down)为更具体的类型。
let maybe: unknown;
maybe = 10;
maybe = "hello";
// console.log(maybe.length); // Error: Object is of type 'unknown'.
if (typeof maybe === "string") {
  // 在这个块中,TypeScript 知道 maybe 是 string 类型
  console.log(maybe.length); // OK
}
// 使用类型断言
let strLength: number = (maybe as string).length; // OK, but potentially unsafe if maybe is not a string at runtime
2.9 void
void 表示没有任何类型。通常用作不返回任何值的函数的返回值类型。
function warnUser(): void {
  console.log("This is my warning message");
  // return undefined; // OK
  // return null; // OK if strictNullChecks is false
  // return "hello"; // Error: Type 'string' is not assignable to type 'void'.
}
let unusable: void = undefined; // 可以赋值为 undefined 或 null (取决于 strictNullChecks)
// unusable = null; // OK if strictNullChecks is false
2.10 null 和 undefined
默认情况下 null 和 undefined 是所有类型的子类型,可以赋值给任何类型。但当启用 strictNullChecks 编译选项时,null 和 undefined 只能赋值给 void 和它们各自的类型。通常推荐启用 strictNullChecks。
// 启用 strictNullChecks: true
let u: undefined = undefined;
let n: null = null;
// let num: number = undefined; // Error
// let str: string = null;     // Error
// 可以使用联合类型显式允许 null 或 undefined
let nullableString: string | null = null;
nullableString = "hello";
let undefinableNumber: number | undefined = undefined;
undefinableNumber = 100;
2.11 never
never 类型表示的是那些永不存在的值的类型。例如,never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式(如无限循环)的返回值类型。never 类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是 never 的子类型或可以赋值给 never 类型(除了 never 本身之外)。
// Function returning never must not have a reachable end point
function error(message: string): never {
  throw new Error(message);
}
// Inferred return type is never
function fail() {
  return error("Something failed");
}
// Function returning never must not have a reachable end point
function infiniteLoop(): never {
  while (true) {}
}
2.12 object
object 表示非原始类型,也就是除了 number, string, boolean, symbol, null, undefined 之外的类型。
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create([1, 2, 3]); // OK
create(function () {}); // OK
// create(42); // Error: Argument of type 'number' is not assignable to parameter of type 'object | null'.
// create("string"); // Error
// create(false); // Error
// create(undefined); // Error (if strictNullChecks is true)
注意: 更常用的是 interface 或具体的对象类型字面量来描述对象结构。
3. 接口 (Interfaces)
接口是 TypeScript 的核心原则之一,用于定义对象的“形状”或“契约”。它们用于类型检查,强制某些结构必须存在。
3.1 定义接口
使用 interface 关键字定义。
interface Person {
  firstName: string;
  lastName: string;
  age: number;
}
function greet(person: Person) {
  console.log(`Hello, ${person.firstName} ${person.lastName}`);
}
let user: Person = { firstName: "Jane", lastName: "User", age: 30 };
greet(user); // OK
// let badUser = { firstName: "John" }; // Error: Property 'lastName' is missing... Property 'age' is missing...
// greet(badUser);
3.2 可选属性 (Optional Properties)
属性名后加 ? 表示该属性是可选的。
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 mySquare1 = createSquare({ color: "black" });
let mySquare2 = createSquare({ width: 20 });
let mySquare3 = createSquare({});
console.log(mySquare1, mySquare2, mySquare3);
3.3 只读属性 (Readonly Properties)
属性名前加 readonly 表示该属性只能在对象首次创建时赋值,之后不能修改。
interface Point {
  readonly x: number;
  readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
// p1.x = 5; // Error: Cannot assign to 'x' because it is a read-only property.
3.4 函数类型 (Function Types)
接口也可以描述函数类型。
interface SearchFunc {
  (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function (src, sub) {
  // 参数名不需要匹配
  let result = src.search(sub);
  return result > -1;
};
console.log(mySearch("hello world", "world")); // true
3.5 可索引类型 (Indexable Types)
接口可以描述那些能够通过索引得到的类型,如数组和对象。
interface StringArray {
  [index: number]: string; // 索引签名:数字索引返回字符串
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0]; // OK
interface Dictionary {
  [key: string]: any; // 索引签名:字符串索引返回任意类型
  length: number; // 可以有其他确定名称的属性
  // name: string; // Error: Property 'name' of type 'string' is not assignable to 'string' index type 'any'.
  // (If index signature type is more specific, e.g., string, then named properties must also be assignable)
}
let myDict: Dictionary = { prop1: "value1", prop2: 100, length: 2 };
console.log(myDict["prop1"]);
3.6 类类型 (Class Types) - 实现接口
接口可以强制一个类去符合某种契约。使用 implements 关键字。
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) {}
}
let clock = new Clock(10, 30);
console.log(clock.currentTime);
3.7 接口继承 (Extending Interfaces)
接口可以像类一样相互继承,允许你复用接口定义。
interface Shape {
  color: string;
}
interface PenStroke {
  penWidth: number;
}
interface Square extends Shape, PenStroke {
  // 可以继承多个接口
  sideLength: number;
}
let square = {} as Square; // 类型断言
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
4. 类 (Classes)
TypeScript 支持基于类的面向对象编程,包括 ES6 中的类特性,并添加了如访问修饰符等功能。
4.1 基本定义
使用 class 关键字。
class Greeter {
  greeting: string; // 属性
  constructor(message: string) {
    // 构造函数
    this.greeting = message;
  }
  greet() {
    // 方法
    return "Hello, " + this.greeting;
  }
}
let greeter = new Greeter("world");
console.log(greeter.greet());
4.2 继承 (Inheritance)
使用 extends 关键字实现继承。子类可以覆盖父类的方法,并使用 super 关键字调用父类的构造函数和方法。
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);
  } // 必须调用 super()
  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();
tom.move(34);
4.3 访问修饰符 (Access Modifiers)
public(默认): 成员可以在任何地方被访问。private: 成员只能在其声明的类内部访问。protected: 成员只能在其声明的类及其子类中访问。
class Person {
  public name: string; // 显式 public
  private age: number;
  protected gender: string;
  constructor(name: string, age: number, gender: string) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
  public getAge() {
    return this.age; // OK, 在类内部访问 private
  }
}
class Employee extends Person {
  private department: string;
  constructor(name: string, age: number, gender: string, department: string) {
    super(name, age, gender);
    this.department = department;
  }
  public getInfo() {
    // console.log(this.age); // Error: Property 'age' is private and only accessible within class 'Person'.
    console.log(
      `Name: ${this.name}, Gender: ${this.gender}, Dept: ${this.department}`
    ); // OK: public name, protected gender
  }
}
let person = new Person("Alice", 30, "female");
console.log(person.name); // OK
// console.log(person.age); // Error: Property 'age' is private...
// console.log(person.gender); // Error: Property 'gender' is protected...
console.log(person.getAge()); // OK
let emp = new Employee("Bob", 25, "male", "Sales");
emp.getInfo();
参数属性 (Parameter Properties): 可以在构造函数参数前添加访问修饰符,自动创建同名属性并赋值。
class Octopus {
  readonly numberOfLegs: number = 8;
  // 将 name 声明和赋值合并到构造函数参数
  constructor(readonly name: string) {}
}
let dad = new Octopus("Man with the 8 strong legs");
// dad.name = "New Name"; // Error! name is readonly.
console.log(dad.name);
4.4 只读修饰符 (Readonly Modifier)
readonly 关键字可以用在属性上,表示该属性只能在声明时或构造函数里被初始化。
class TestReadonly {
  readonly value: string;
  constructor(val: string) {
    this.value = val; // OK in constructor
  }
  changeValue() {
    // this.value = "new value"; // Error: Cannot assign to 'value' because it is a read-only property.
  }
}
4.5 存取器 (Accessors - Getters / Setters)
允许你像访问属性一样调用函数,用于控制属性的读取和设置。
let passcode = "secret passcode";
class EmployeePass {
  private _fullName: string = "";
  get fullName(): string {
    console.log("Getter called");
    return this._fullName;
  }
  set fullName(newName: string) {
    console.log("Setter called");
    if (passcode && passcode === "secret passcode") {
      this._fullName = newName;
    } else {
      console.log("Error: Unauthorized update of employee!");
    }
  }
}
let employee = new EmployeePass();
employee.fullName = "Bob Smith"; // 调用 setter
if (employee.fullName) {
  // 调用 getter
  console.log(employee.fullName);
}
4.6 静态属性和方法 (Static Members)
使用 static 关键字定义的属性和方法属于类本身,而不是类的实例。通过类名直接访问。
class Grid {
  static origin = { x: 0, y: 0 }; // 静态属性
  calculateDistanceFromOrigin(point: { x: number; y: number }) {
    let xDist = point.x - Grid.origin.x; // 访问静态属性
    let yDist = point.y - Grid.origin.y;
    return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
  }
  static createDefaultGrid() {
    // 静态方法
    return new Grid(1.0);
  }
  constructor(public scale: number) {}
}
let grid1 = new Grid(1.0);
let grid2 = new Grid(5.0);
console.log(Grid.origin); // { x: 0, y: 0 } - 通过类名访问
let defaultGrid = Grid.createDefaultGrid(); // 调用静态方法
console.log(defaultGrid.calculateDistanceFromOrigin({ x: 10, y: 10 }));
4.7 抽象类 (Abstract Classes)
抽象类作为其他派生类的基类使用。它们一般不会直接被实例化。不同于接口,抽象类可以包含成员的实现细节。abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法。抽象方法必须在派生类中实现。
abstract class Department {
  constructor(public name: string) {}
  printName(): void {
    console.log("Department name: " + this.name);
  }
  abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
  constructor() {
    super("Accounting and Auditing"); // 在派生类的构造函数中必须调用 super()
  }
  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(); // Error: Cannot create an instance of an abstract class.
department = new AccountingDepartment(); // OK
department.printName();
department.printMeeting();
// department.generateReports(); // Error: Property 'generateReports' does not exist on type 'Department'. (需要类型断言或用子类类型引用)
(department as AccountingDepartment).generateReports(); // OK with type assertion
5. 函数 (Functions)
TypeScript 为 JavaScript 函数添加了类型注解和其他特性。
5.1 函数类型注解
可以为函数的参数和返回值添加类型注解。
// 命名函数
function add(x: number, y: number): number {
  return x + y;
}
// 匿名函数 (函数表达式)
let myAdd = function (x: number, y: number): number {
  return x + y;
};
// 也可以只注解变量类型,TS 会尝试推断
let mySubtract: (baseValue: number, increment: number) => number = function (
  x,
  y
) {
  return x - y;
};
5.2 可选参数和默认参数
- 可选参数: 参数名后加 
?。可选参数必须位于必选参数之后。 - 默认参数: 参数后加 
= 默认值。带默认值的参数被视为可选参数。 
function buildName(firstName: string, lastName?: string): string {
  // lastName is optional
  if (lastName) {
    return firstName + " " + lastName;
  } else {
    return firstName;
  }
}
let result1 = buildName("Bob"); // OK, lastName is undefined
let result2 = buildName("Bob", "Adams"); // OK
// let result3 = buildName("Bob", "Adams", "Sr."); // Error: Expected 2 arguments, but got 3.
function calculatePay(rate: number, hours: number = 40): number {
  // hours has default value
  return rate * hours;
}
let pay1 = calculatePay(15); // Uses default hours (40) -> 600
let pay2 = calculatePay(15, 20); // Uses provided hours (20) -> 300
5.3 剩余参数 (Rest Parameters)
用于将多个参数收集到一个变量中(必须是数组类型)。剩余参数必须是参数列表中的最后一个。
function buildFullName(firstName: string, ...restOfName: string[]): string {
  return firstName + " " + restOfName.join(" ");
}
let employeeName = buildFullName("Joseph", "Samuel", "Lucas", "MacKinzie");
console.log(employeeName); // "Joseph Samuel Lucas MacKinzie"
5.4 this 参数
有时需要明确指定函数中 this 的类型。this 参数是一个假的参数,它出现在参数列表的最前面。
interface Card {
  suit: string;
  card: number;
}
interface Deck {
  suits: string[];
  cards: number[];
  createCardPicker(this: Deck): () => Card; // Explicitly defining 'this' type for the function
}
let deck: Deck = {
  suits: ["hearts", "spades", "clubs", "diamonds"],
  cards: Array(52),
  // NOTE: The function now explicitly specifies that its callee must be of type Deck
  createCardPicker: function (this: Deck) {
    return () => {
      let pickedCard = Math.floor(Math.random() * 52);
      let pickedSuit = Math.floor(pickedCard / 13);
      // 'this' here refers to the 'Deck' object because of the arrow function's lexical scoping
      return { suit: this.suits[pickedSuit], card: pickedCard % 13 };
    };
  },
};
let cardPicker = deck.createCardPicker();
let pickedCard = cardPicker();
console.log("card: " + pickedCard.card + " of " + pickedCard.suit);
5.5 函数重载 (Overloads)
允许为同一个函数根据不同的参数类型或数量提供多个函数签名(类型定义),然后提供一个通用的实现。
// Overload signatures
function processInput(input: string): string;
function processInput(input: number): number;
function processInput(input: boolean): boolean;
// Implementation signature (must be compatible with all overloads)
function processInput(
  input: string | number | boolean
): string | number | boolean {
  if (typeof input === "string") {
    return `Processed string: ${input}`;
  } else if (typeof input === "number") {
    return input * 2;
  } else {
    return !input;
  }
}
let resultStr = processInput("hello"); // Type is inferred as string
let resultNum = processInput(10); // Type is inferred as number
let resultBool = processInput(true); // Type is inferred as boolean
console.log(resultStr);
console.log(resultNum);
console.log(resultBool);
// let resultErr = processInput(null); // Error: No overload matches this call.
注意: 实现签名 (function processInput(input: any)... 或使用联合类型) 本身不能被直接调用。编译器只认重载签名。
6. 泛型 (Generics)
泛型允许你编写可重用的、适用于多种类型的组件(如函数、类、接口),同时保持类型安全。
6.1 泛型函数
使用类型变量(如 <T>)来捕获用户提供的类型。
// <T> is the type parameter declaration
function identity<T>(arg: T): T {
  return arg;
}
// Calling the generic function
let output1 = identity<string>("myString"); // Explicitly specify type T as string
let output2 = identity("myString"); // Type inference: T is inferred as string
let output3 = identity<number>(100); // Explicitly specify type T as number
let output4 = identity(100); // Type inference: T is inferred as number
console.log(typeof output1, output1); // string myString
console.log(typeof output4, output4); // number 100
6.2 泛型接口
接口也可以是泛型的。
interface GenericIdentityFn<T> {
  (arg: T): T;
}
function identityAgain<T>(arg: T): T {
  return arg;
}
let myIdentity: GenericIdentityFn<number> = identityAgain;
console.log(myIdentity(5)); // 5
// console.log(myIdentity("hello")); // Error: Argument of type 'string' is not assignable to parameter of type 'number'.
6.3 泛型类
类也可以是泛型的。
class GenericNumber<T> {
  zeroValue: T | undefined;
  add: ((x: T, y: T) => T) | undefined;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
  return x + y;
};
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 10)); // 10
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function (x, y) {
  return x + y;
};
console.log(stringNumeric.add(stringNumeric.zeroValue, "test")); // "test"
6.4 泛型约束 (Generic Constraints)
有时你希望限制泛型类型变量可以接受的类型范围。可以使用 extends 关键字添加约束。
interface Lengthwise {
  length: number; // Constraint: T must have a 'length' property
}
// T must have a .length property
function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // Now we know it has a .length property, so no error
  return arg;
}
loggingIdentity("hello"); // OK, string has length
loggingIdentity([1, 2, 3]); // OK, array has length
loggingIdentity({ length: 10, value: 3 }); // OK, object has length property
// loggingIdentity(3); // Error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'. (number doesn't have length)
// loggingIdentity({ value: 3 }); // Error: Property 'length' is missing in type '{ value: number; }' but required in type 'Lengthwise'.
7. 类型推断 (Type Inference)
TypeScript 编译器可以在很多情况下自动推断出变量或表达式的类型,无需显式注解。
let x = 3; // TypeScript infers x as 'number'
// x = "hello"; // Error: Type 'string' is not assignable to type 'number'.
let y = [0, 1, null]; // TypeScript infers y as '(number | null)[]'
// Contextual Typing: TS infers parameter types based on context
window.onmousedown = function (mouseEvent /* Inferred as MouseEvent */) {
  // console.log(mouseEvent.button); // OK
  // console.log(mouseEvent.kangaroo); // Error: Property 'kangaroo' does not exist on type 'MouseEvent'.
};
// Best Common Type: TS finds the best common type for multiple expressions
let arr = [0, 1, "hello", null]; // Inferred as (string | number | null)[]
8. 类型断言 (Type Assertions)
类型断言允许你覆盖 TypeScript 的类型推断,告诉编译器“相信我,我知道这个变量的类型”。它类似于其他语言中的类型转换,但没有运行时的影响,仅用于编译时检查。有两种语法:
- 尖括号语法: 
<Type>value(在 JSX 中不能使用) as语法:value as Type(推荐,尤其是在 React/JSX 项目中)
let someValue: any = "this is a string";
// 1. Angle-bracket syntax
let strLength1: number = (<string>someValue).length;
// 2. as-syntax (preferred)
let strLength2: number = (someValue as string).length;
console.log(strLength1, strLength2);
// Be careful: Assertions bypass checks, can lead to runtime errors if wrong
let potentiallyWrong = 123;
// console.log((potentiallyWrong as string).length); // Compiles, but throws TypeError at runtime
9. 高级类型 (Advanced Types)
9.1 联合类型 (Union Types)
表示一个值可以是几种类型之一,使用 | 分隔。
function printId(id: number | string) {
  console.log("Your ID is: " + id);
  // Need type guard to use type-specific methods
  if (typeof id === "string") {
    console.log(id.toUpperCase()); // OK, id is string here
  } else {
    console.log(id.toFixed(2)); // OK, id is number here
  }
}
printId(101); // OK
printId("202"); // OK
// printId({ myID: 22342 }); // Error
9.2 交叉类型 (Intersection Types)
将多个类型合并为一个类型,包含所有类型的成员。使用 & 分隔。常用于混入 (mixins) 或其他不适合继承的场景。
interface Loggable {
  log(message: string): void;
}
interface Serializable {
  serialize(): string;
}
type LoggableSerializable = Loggable & Serializable;
class ConsoleLogger implements Loggable {
  log(message: string) {
    console.log(message);
  }
}
class JsonSerialier implements Serializable {
  constructor(private data: any) {}
  serialize(): string {
    return JSON.stringify(this.data);
  }
}
// Helper function to mixin properties
function extend<First extends {}, Second extends {}>(
  first: First,
  second: Second
): First & Second {
  const result: Partial<First & Second> = {};
  for (const prop in first) {
    if (first.hasOwnProperty(prop)) {
      (result as First)[prop] = first[prop];
    }
  }
  for (const prop in second) {
    if (second.hasOwnProperty(prop)) {
      (result as Second)[prop] = second[prop];
    }
  }
  return result as First & Second;
}
class CombinedItem {
  constructor(private data: any) {}
  // Manually implement combined interface
  log(message: string) {
    console.log(`[LOG] ${message}: ${this.serialize()}`);
  }
  serialize(): string {
    return JSON.stringify(this.data);
  }
}
// Using the type
let item: LoggableSerializable = new CombinedItem({ value: 42 });
item.log("Item data");
console.log(item.serialize());
9.3 类型别名 (Type Aliases)
使用 type 关键字为类型创建一个新名字。可以用于原始类型、联合类型、交叉类型、元组或任何其他需要手写类型注解的地方。
type Point = { x: number; y: number };
type ID = string | number;
type StringOrNumberArray = (string | number)[];
type Tree<T> = {
  // Type alias can also be generic
  value: T;
  left?: Tree<T>;
  right?: Tree<T>;
};
let p: Point = { x: 10, y: 20 };
let userId: ID = "user-123";
userId = 456;
let mixedArray: StringOrNumberArray = [1, "two", 3, "four"];
let stringTree: Tree<string> = {
  value: "root",
  left: { value: "left-child" },
};
9.4 字面量类型 (Literal Types)
允许你指定变量必须具有的确切值。常与联合类型结合使用,构成“可辨识联合类型”。
type Easing = "ease-in" | "ease-out" | "ease-in-out"; // String literal union
let animationTiming: Easing = "ease-in"; // OK
// animationTiming = "linear"; // Error: Type '"linear"' is not assignable to type 'Easing'.
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6; // Numeric literal union
let roll: DiceRoll = 4; // OK
// roll = 7; // Error
interface Circle {
  kind: "circle"; // Literal type property acts as a discriminant
  radius: number;
}
interface SquareShape {
  // Renamed to avoid conflict with built-in Square
  kind: "square";
  sideLength: number;
}
type ShapeUnion = Circle | SquareShape; // Discriminated union
function getArea(shape: ShapeUnion) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2; // TS knows shape is Circle here
    case "square":
      return shape.sideLength ** 2; // TS knows shape is SquareShape here
  }
}
console.log(getArea({ kind: "circle", radius: 10 }));
console.log(getArea({ kind: "square", sideLength: 5 }));
9.5 索引类型 (Index Types) 和 映射类型 (Mapped Types)
- 索引类型查询 (
keyof): 获取一个类型所有公共属性名的联合类型。 - 索引访问类型 (
T[K]): 获取类型T的属性K的类型。 - 映射类型: 基于一个现有类型创建新类型,对原类型的每个属性应用转换。
 
interface UserProfile {
  id: number;
  name: string;
  email?: string;
  isAdmin: boolean;
}
// 1. keyof
type UserProfileKeys = keyof UserProfile; // "id" | "name" | "email" | "isAdmin"
let key: UserProfileKeys = "name"; // OK
// key = "age"; // Error
// 2. Indexed Access Type (Lookup Type)
type UserIdType = UserProfile["id"]; // number
type UserNameOrEmail = UserProfile["name" | "email"]; // string | string | undefined -> string | undefined
// 3. Mapped Types
// Example: Make all properties readonly
type ReadonlyUserProfile = {
  readonly [P in keyof UserProfile]: UserProfile[P];
};
// Equivalent to built-in Readonly<UserProfile>
// Example: Make all properties optional
type PartialUserProfile = {
  [P in keyof UserProfile]?: UserProfile[P];
};
// Equivalent to built-in Partial<UserProfile>
// Example: Create a flags object based on keys
type UserProfileFlags = {
  [P in keyof UserProfile]: boolean;
};
/* Resulting type:
{
    id: boolean;
    name: boolean;
    email?: boolean; // Optionality is preserved if source has it
    isAdmin: boolean;
}
*/
let userFlags: UserProfileFlags = { id: true, name: false, isAdmin: true }; // email is optional
// More complex example: Nullable properties
type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};
type NullableUserProfile = Nullable<UserProfile>;
let nullableUser: NullableUserProfile = {
  id: 1,
  name: null,
  email: "test@example.com",
  isAdmin: null,
};
9.6 条件类型 (Conditional Types)
允许根据条件选择两种可能的类型之一,形式为 T extends U ? X : Y。
// Example: Extract specific types from a union
type NonNullable<T> = T extends null | undefined ? never : T;
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]
// Example: Flatten array type
type Flatten<T> = T extends Array<infer Item> ? Item : T;
// 'infer Item' declares a new generic type variable 'Item' within the 'true' branch
type Str = Flatten<string[]>; // string
type Num = Flatten<number>; // number (not an array, so returns T itself)
// Example within a generic function signature
declare function process<T extends any[] | string>(
  data: T
): T extends any[] ? T[number] : T;
let item = process([1, 2, 3]); // item type is 'number' (T[number] for array)
let strItem = process("hello"); // strItem type is 'string' (T for string)
// Built-in conditional types: Exclude, Extract, NonNullable, ReturnType, InstanceType
type T2 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T3 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T4 = ReturnType<() => string>; // string
9.7 工具类型 (Utility Types)
TypeScript 提供了一些内置的工具类型,用于常见的类型转换(许多基于映射类型和条件类型实现)。
Partial<T>: 将T的所有属性设为可选。Required<T>: 将T的所有属性设为必选。Readonly<T>: 将T的所有属性设为只读。Record<K, T>: 创建一个对象类型,其属性键为K类型,属性值为T类型。Pick<T, K>: 从T中选择一组属性K来构造新类型。Omit<T, K>: 从T中排除一组属性K来构造新类型。Exclude<T, U>: 从T中排除可分配给U的类型。Extract<T, U>: 从T中提取可分配给U的类型。NonNullable<T>: 从T中排除null和undefined。Parameters<T>: 获取函数类型T的参数类型组成的元组。ConstructorParameters<T>: 获取构造函数类型T的参数类型组成的元组。ReturnType<T>: 获取函数类型T的返回值类型。InstanceType<T>: 获取构造函数类型T的实例类型。
interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}
// Partial: Make all properties optional
type PartialTodo = Partial<Todo>;
// { title?: string; description?: string; completed?: boolean; createdAt?: number; }
// Required: Make all properties required (useful if original had optionals)
// type RequiredTodo = Required<PartialTodo>; // { title: string; description: string; completed: boolean; createdAt: number; }
// Readonly: Make all properties readonly
type ReadonlyTodo = Readonly<Todo>;
// { readonly title: string; readonly description: string; readonly completed: boolean; readonly createdAt: number; }
// Record: Create a dictionary-like type
type PageInfo = { title: string; visited: boolean };
type Pages = "home" | "about" | "contact";
const navInfo: Record<Pages, PageInfo> = {
  home: { title: "Home", visited: true },
  about: { title: "About", visited: false },
  contact: { title: "Contact", visited: false },
};
// Pick: Select specific properties
type TodoPreview = Pick<Todo, "title" | "completed">;
// { title: string; completed: boolean; }
// Omit: Remove specific properties
type TodoInfo = Omit<Todo, "completed" | "createdAt">;
// { title: string; description: string; }
// Parameters: Get function parameter types
type PointPrinterParams = Parameters<(p: { x: number; y: number }) => void>; // [(p: { x: number; y: number })]
let params: PointPrinterParams = [{ x: 1, y: 2 }];
// ReturnType: Get function return type
type GreetingReturnType = ReturnType<() => string>; // string
let greetingResult: GreetingReturnType = "hello";
10. 模块 (Modules)
TypeScript 沿用了 ES6 (ES2015) 的模块系统。任何包含顶级 import 或 export 的文件都被视为一个模块。
10.1 导出 (Export)
使用 export 关键字导出变量、函数、类、接口、类型别名等。
// --- StringValidator.ts ---
export interface StringValidator {
  isAcceptable(s: string): boolean;
}
export const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
  isAcceptable(s: string) {
    return lettersRegexp.test(s);
  }
}
// Export statement
const numberRegexp = /^[0-9]+$/;
export { numberRegexp };
// Re-export
// export { ZipCodeValidator as RegExpBasedZipCodeValidator } from "./ZipCodeValidator";
// Default export (only one per module)
export default class DefaultValidator implements StringValidator {
  isAcceptable(s: string): boolean {
    return s.length > 0;
  }
}
10.2 导入 (Import)
使用 import 关键字导入其他模块导出的内容。
// --- TestValidators.ts ---
// Import specific named exports
import {
  LettersOnlyValidator,
  lettersRegexp as letters,
} from "./StringValidator";
// Import using namespace
import * as validator from "./StringValidator";
// Import default export
import DefaultValidator from "./StringValidator";
let myString = "Hello";
let lettersValidator = new LettersOnlyValidator();
console.log(`LettersOnlyValidator: ${lettersValidator.isAcceptable(myString)}`); // true
let numberValidator = new validator.LettersOnlyValidator(); // Using namespace import
console.log(
  `Namespace Import - LettersOnly: ${numberValidator.isAcceptable("12345")}`
); // false
console.log(`Namespace Import - Regexp: ${validator.numberRegexp.test("123")}`); // true
console.log(`Imported regexp: ${letters.test(myString)}`); // true
let defaultVal = new DefaultValidator();
console.log(`Default Validator: ${defaultVal.isAcceptable("")}`); // false
11. 命名空间 (Namespaces)
在 ES6 模块成为标准之前,TypeScript 使用 namespace (早期版本叫 internal module) 来组织代码,避免全局命名冲突。虽然现在推荐使用 ES6 模块,但在某些场景(如组织大型项目内部结构或编写 .d.ts 文件)仍可能用到。
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);
    }
  }
  export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
      return s.length === 5 && lettersRegexp.test(s); // Mistake here, should be number regexp
    }
  }
}
// Usage
let validators: { [s: string]: Validation.StringValidator } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
console.log(validators["Letters only"].isAcceptable("HelloWorld")); // true
12. 声明文件 (.d.ts)
当你想在 TypeScript 项目中使用纯 JavaScript 库时,需要声明文件 (.d.ts) 来告诉 TypeScript 编译器该库的类型信息(变量、函数、类等)。
12.1 使用已有的声明文件
可以通过 npm 安装社区维护的类型声明包,通常以 @types/ 开头。
npm install --save-dev @types/lodash
然后在 TypeScript 代码中直接 import 和使用库,就像它是用 TypeScript 写的一样。
import _ from "lodash";
console.log(_.shuffle([1, 2, 3, 4]));
12.2 编写自己的声明文件
如果某个库没有类型声明,或者你想为自己的 JavaScript 代码提供类型信息,可以手动编写 .d.ts 文件。
// --- my-library.js --- (A simple JavaScript library)
/*
module.exports = {
  greet: function(name) {
    return "Hello, " + name;
  },
  version: "1.0.0"
};
*/
// --- my-library.d.ts --- (Declaration file for the library)
declare module "my-library" {
  // Declare the module name
  export function greet(name: string): string; // Declare exported function signature
  export const version: string; // Declare exported constant type
}
// --- main.ts --- (Using the library with declarations)
import { greet, version } from "my-library";
console.log(greet("TypeScript")); // OK, type checking works
console.log("Library version:", version); // OK
// greet(123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.
13. tsconfig.json 配置文件
tsconfig.json 文件位于项目的根目录,用于指定 TypeScript 编译器的选项和项目文件。
13.1 主要配置项
compilerOptions: 包含各种编译选项。target: 指定编译后的 ECMAScript 目标版本 (e.g., "es5", "es2016", "esnext").module: 指定模块系统 (e.g., "commonjs", "es2015", "esnext").outDir: 指定编译后.js文件的输出目录。rootDir: 指定输入.ts文件根目录(用于控制输出结构)。strict: 启用所有严格类型检查选项 (推荐)。包括noImplicitAny,strictNullChecks,strictFunctionTypes,strictBindCallApply,strictPropertyInitialization,noImplicitThis,alwaysStrict。esModuleInterop: 允许通过import x from 'module'的方式导入 CommonJS 模块 (如果模块没有export default)。declaration: 生成相应的.d.ts声明文件。sourceMap: 生成.map文件,用于调试。lib: 指定编译时需要包含的库文件 (e.g., ["dom", "es2015"]).jsx: 配置 JSX 支持 ("preserve", "react", "react-native").
include: 指定需要编译的文件或目录模式 (e.g., ["src/**/*"]).exclude: 指定不需要编译的文件或目录模式 (e.g., ["node_modules", "**/*.spec.ts"]).files: 指定一个明确的文件列表进行编译。
13.2 示例 tsconfig.json
{
  "compilerOptions": {
    /* Basic Options */
    "target": "es2016", // Specify ECMAScript target version
    "module": "commonjs", // Specify module code generation
    "lib": ["es2016", "dom"], // Specify library files to be included
    "outDir": "./dist", // Redirect output structure to the directory
    "rootDir": "./src", // Specify the root directory of input files
    /* Strict Type-Checking Options */
    "strict": true, // Enable all strict type-checking options
    // "noImplicitAny": true,     // Raise error on expressions and declarations with an implied 'any' type
    // "strictNullChecks": true,  // Enable strict null checks
    // "strictFunctionTypes": true, // Enable strict checking of function types
    // "strictPropertyInitialization": true, // Enable strict checking for property initialization in classes
    // "noImplicitThis": true,    // Raise error on 'this' expressions with an implied 'any' type
    // "alwaysStrict": true,      // Parse in strict mode and emit "use strict" for each source file
    /* Module Resolution Options */
    "moduleResolution": "node", // Specify module resolution strategy
    "baseUrl": "./", // Base directory to resolve non-absolute module names
    "paths": {
      // Series of entries which re-map imports to lookup locations
      "@/*": ["src/*"]
    },
    "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules
    "allowSyntheticDefaultImports": true, // Allow default imports from modules with no default export
    /* Source Map Options */
    "sourceMap": true, // Generates corresponding '.map' file
    /* Advanced Options */
    "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file.
    "skipLibCheck": true // Skip type checking of all declaration files (*.d.ts).
  },
  "include": [
    "src/**/*" // Include all files in the src directory
  ],
  "exclude": [
    "node_modules", // Exclude node_modules
    "**/*.spec.ts", // Exclude test files
    "dist" // Exclude output directory
  ]
}