TS语法详解

2 阅读7分钟

TypeScript 语法详解

1. 基础类型

1.1 基本类型

// 布尔值
let isDone: boolean = true;

// 数字(支持十进制、十六进制、二进制、八进制)
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

// 字符串
let color: string = "blue";
let fullName: string = `Bob Bobbington`;
let sentence: string = `Hello, my name is ${fullName}`;

// 数组(两种写法)
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];

// 元组 Tuple
let tuple: [string, number] = ["hello", 10];
// 访问元组元素
let str: string = tuple[0];
let num: number = tuple[1];

// 枚举
enum Color {
  Red = 1,    // 可以从1开始编号
  Green,      // 2
  Blue = 4    // 可以手动赋值
}
let c: Color = Color.Green;  // 2

// Any(任意类型)
let notSure: any = 4;
notSure = "maybe a string";
notSure = false;

// Void(无任何类型)
function warnUser(): void {
  console.log("This is a warning");
}

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

// Never(永不存在的值的类型)
function error(message: string): never {
  throw new Error(message);
}
function infiniteLoop(): never {
  while (true) {}
}

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

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

1.2 字面量类型

// 字符串字面量类型
type Direction = "north" | "south" | "east" | "west";
let dir: Direction = "north"; // 只能赋这四个值

// 数字字面量类型
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
let dice: Dice = 3;

// 布尔字面量类型
type Success = true;
let success: Success = true;

// 模板字面量类型
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiPath = `/api/${HttpMethod}`;
let path: ApiPath = "/api/GET";

// 结合使用
type EventName = "click" | "mouseover" | "keydown";
type EventHandler = (event: Event) => void;

2. 变量声明

// let 和 const
let variable: string = "can be reassigned";
const constant: string = "cannot be reassigned";

// 解构赋值
let input: [number, number] = [1, 2];
let [first, second] = input;

// 对象解构
let o = { a: "foo", b: 12, c: "bar" };
let { a, b } = o;
let { a: newName1, b: newName2 } = o; // 重命名

// 函数参数解构
function f({ a, b = 0 } = { a: "" }): void {
  // ...
}
f({ a: "yes" });

// 展开运算符
let firstArray = [1, 2];
let secondArray = [3, 4];
let bothPlus = [0, ...firstArray, ...secondArray, 5];

// 对象展开
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };

3. 接口

3.1 对象接口

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

// 使用接口
let person: Person = {
  name: "Alice",
  age: 30,
  id: 12345
};

// 接口继承
interface Employee extends Person {
  employeeId: number;
  department: string;
}

// 混合类型接口
interface Counter {
  (start: number): string; // 函数
  interval: number;        // 属性
  reset(): void;           // 方法
}

// 可索引接口
interface StringArray {
  [index: number]: string;
}
let myArray: StringArray = ["Bob", "Fred"];

3.2 函数类型接口

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

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

// 使用类型别名定义函数类型
type SearchFunction = (source: string, subString: string) => boolean;

3.3 类类型接口

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) { }
}

3.4 接口扩展

interface Shape {
  color: string;
}

interface PenStroke {
  penWidth: number;
}

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

let square: Square = {
  color: "blue",
  penWidth: 5.0,
  sideLength: 10
};

4.

4.1 基本类

class Greeter {
  greeting: string;
  
  constructor(message: string) {
    this.greeting = message;
  }
  
  greet(): string {
    return `Hello, ${this.greeting}`;
  }
}

let greeter = new Greeter("world");

4.2 继承

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

class Snake extends Animal {
  constructor(name: string) {
    super(name); // 必须调用父类构造函数
  }
  
  move(distanceInMeters = 5): void {
    console.log("Slithering...");
    super.move(distanceInMeters); // 调用父类方法
  }
}

4.3 访问修饰符

class Person {
  public name: string;        // 公共,默认修饰符
  private age: number;        // 私有,只能在类内部访问
  protected id: string;       // 受保护,只能在类及其子类中访问
  readonly country: string;   // 只读
  
  constructor(name: string, age: number, id: string) {
    this.name = name;
    this.age = age;
    this.id = id;
    this.country = "China";
  }
  
  public getAge(): number {
    return this.age; // 可以访问私有属性
  }
}

class Employee extends Person {
  private department: string;
  
  constructor(name: string, age: number, id: string, department: string) {
    super(name, age, id);
    this.department = department;
  }
  
  public getDetails(): string {
    return `${this.name} (${this.id}) works in ${this.department}`;
    // this.age 无法访问(私有)
    // this.id 可以访问(受保护)
  }
}

4.4 参数属性

class Animal {
  constructor(private name: string, public age: number) {
    // 自动创建并初始化属性
  }
  
  getName(): string {
    return this.name; // 可以直接访问
  }
}

// 等同于
class AnimalTraditional {
  private name: string;
  public age: number;
  
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

4.5 Getter/Setter

class Employee {
  private _fullName: string = "";
  
  get fullName(): string {
    return this._fullName;
  }
  
  set fullName(newName: string) {
    if (newName && newName.length > 0) {
      this._fullName = newName;
    } else {
      throw new Error("Invalid name");
    }
  }
}

let employee = new Employee();
employee.fullName = "Alice Smith"; // 调用 setter
console.log(employee.fullName);    // 调用 getter

4.6 静态属性

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

4.7 抽象类

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");
  }
  
  printMeeting(): void {
    console.log("The Accounting Department meets each Monday at 10am.");
  }
}

// let dept = new Department(); // 错误:无法创建抽象类的实例
let dept: Department = new AccountingDepartment(); // 允许

5. 函数

5.1 函数类型

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

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

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

// 可选参数
function buildName(firstName: string, lastName?: string): string {
  return lastName ? `${firstName} ${lastName}` : firstName;
}

// 默认参数
function buildName2(firstName: string, lastName = "Smith"): string {
  return `${firstName} ${lastName}`;
}

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

5.2 函数重载

// 重载签名
function pickCard(x: { suit: string; card: number; }[]): number;
function pickCard(x: number): { suit: string; card: number; };

// 实现签名
function pickCard(x: any): any {
  if (Array.isArray(x)) {
    return x.length;
  } else if (typeof x === "number") {
    return { suit: "hearts", card: x % 13 };
  }
}

let myDeck = [{ suit: "diamonds", card: 2 }, { suit: "spades", card: 10 }];
let pickedCard1 = pickCard(myDeck);    // number
let pickedCard2 = pickCard(15);        // {suit: string; card: number;}

5.3 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], // 正确绑定 this
        card: pickedCard % 13
      };
    };
  }
};

6. 泛型

6.1 泛型函数

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

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

// 多个类型参数
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}
let swapped = swap([7, "hello"]); // ["hello", 7]

// 泛型约束
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}
loggingIdentity({ length: 10, value: 3 }); // 正确
// loggingIdentity(3); // 错误:数字没有length属性

// 在泛型约束中使用类型参数
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
let x = { a: 1, b: 2, c: 3 };
getProperty(x, "a"); // 正确
// getProperty(x, "m"); // 错误:"m"不是x的属性

6.2 泛型接口

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

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

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

6.3 泛型默认类型

interface A<T = string> {
  name: T;
}

const a: A = { name: "hello" };     // 使用默认类型 string
const b: A<number> = { name: 123 }; // 指定类型 number

7. 枚举

7.1 数字枚举

enum Direction {
  Up = 1,    // 从1开始编号
  Down,      // 2
  Left,      // 3
  Right      // 4
}

// 编译为:
// var Direction;
// (function (Direction) {
//     Direction[Direction["Up"] = 1] = "Up";
//     Direction[Direction["Down"] = 2] = "Down";
//     Direction[Direction["Left"] = 3] = "Left";
//     Direction[Direction["Right"] = 4] = "Right";
// })(Direction || (Direction = {}));

7.2 字符串枚举

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}

7.3 异构枚举(混合字符串和数字)

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES",
}

7.4 常量枚举

const enum Direction {
  Up,
  Down,
  Left,
  Right
}

let directions = [
  Direction.Up,    // 编译时被替换为 0
  Direction.Down,  // 1
  Direction.Left,  // 2
  Direction.Right  // 3
];

7.5 外部枚举

declare enum Enum {
  A = 1,
  B,
  C = 2
}

8. 高级类型

8.1 联合类型

type StringOrNumber = string | number;

function padLeft(value: string, padding: StringOrNumber): string {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  return padding + value;
}

8.2 交叉类型

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

interface Employee {
  company: string;
  salary: number;
}

type EmployeePerson = Person & Employee;

let employee: EmployeePerson = {
  name: "Alice",
  age: 30,
  company: "Tech Corp",
  salary: 50000
};

8.3 类型保护

// typeof 类型保护
function padLeft(value: string, padding: string | number): string {
  if (typeof padding === "number") {
    // 这里 padding 是 number 类型
    return Array(padding + 1).join(" ") + value;
  }
  // 这里 padding 是 string 类型
  return padding + value;
}

// instanceof 类型保护
class Bird {
  fly() { console.log("flying"); }
  layEggs() { console.log("laying eggs"); }
}

class Fish {
  swim() { console.log("swimming"); }
  layEggs() { console.log("laying eggs"); }
}

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

let pet = getSmallPet();
if (pet instanceof Bird) {
  pet.fly(); // 正确:pet 是 Bird 类型
} else {
  pet.swim(); // 正确:pet 是 Fish 类型
}

// 自定义类型保护
interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

function isFish(pet: Bird | Fish): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

if (isFish(pet)) {
  pet.swim(); // pet 是 Fish 类型
} else {
  pet.fly(); // pet 是 Bird 类型
}

8.4 索引类型

// keyof 操作符
interface Person {
  name: string;
  age: number;
  location: string;
}

type PersonKeys = keyof Person; // "name" | "age" | "location"

// 索引访问操作符 T[K]
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

let person: Person = { name: "Alice", age: 30, location: "NYC" };
let name: string = getProperty(person, "name"); // string
let age: number = getProperty(person, "age");   // number

// 映射类型
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

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

type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;

8.5 条件类型

// 基本条件类型
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 Diff<T, U> = T extends U ? never : T;
type Filter<T, U> = T extends U ? T : never;

type T3 = Diff<"a" | "b" | "c", "a" | "d">; // "b" | "c"
type T4 = Filter<"a" | "b" | "c", "a" | "d">; // "a"

// 条件类型中的推断
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type T5 = ReturnType<() => string>; // string
type T6 = ReturnType<(x: number) => number[]>; // number[]

// 内置条件类型
type T7 = Exclude<"a" | "b" | "c", "a" | "d">; // "b" | "c"
type T8 = Extract<"a" | "b" | "c", "a" | "d">; // "a"
type T9 = NonNullable<string | number | undefined>; // string | number
type T10 = InstanceType<typeof Array>; // any[]

9. 模块

9.1 导出

// math.ts
// 导出声明
export const pi = 3.14;
export interface Circle {
  radius: number;
}
export class Calculator {
  static add(x: number, y: number): number {
    return x + y;
  }
}

// 导出语句
const version = "1.0";
export { version };

// 重命名导出
export { version as apiVersion };

// 默认导出(每个模块只能有一个)
export default class MathUtils {
  static multiply(x: number, y: number): number {
    return x * y;
  }
}

9.2 导入

// app.ts
// 导入命名的导出
import { pi, Calculator, type Circle } from "./math";
// 导入整个模块
import * as math from "./math";
// 导入默认导出
import MathUtils from "./math";
// 重命名导入
import { version as apiVersion } from "./math";

// 重新导出
export { pi } from "./math";
export * from "./math";

10. 命名空间

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): boolean {
      return lettersRegexp.test(s);
    }
  }
  
  export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string): boolean {
      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();

// 多文件命名空间
// Validation.ts
namespace Validation {
  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }
}

// LettersOnlyValidator.ts
/// <reference path="Validation.ts" />
namespace Validation {
  const lettersRegexp = /^[A-Za-z]+$/;
  export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
      return lettersRegexp.test(s);
    }
  }
}

11. 装饰器

11.1 类装饰器

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

// 装饰器工厂
function color(value: string) {
  return function (constructor: Function) {
    // 添加元数据等
  };
}

@color("red")
class Car {}

11.2 方法装饰器

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

11.3 访问器装饰器

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

class Point {
  private _x: number;
  private _y: number;
  
  constructor(x: number, y: number) {
    this._x = x;
    this._y = y;
  }
  
  @configurable(false)
  get x() { return this._x; }
  
  @configurable(false)
  get y() { return this._y; }
}

11.4 属性装饰器

function format(formatString: string) {
  return function (target: any, propertyKey: string) {
    // 添加元数据等
  };
}

class Greeter {
  @format("Hello, %s")
  greeting: string;
  
  constructor(message: string) {
    this.greeting = message;
  }
}

11.5 参数装饰器

function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
  // 添加验证逻辑
}

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

12. 声明文件

12.1 声明变量

declare var jQuery: (selector: string) => any;

12.2 声明函数

declare function setTimeout(handler: (...args: any[]) => void, timeout?: number, ...args: any[]): number;

12.3 声明类

declare class Animal {
  name: string;
  constructor(name: string);
  sayHi(): string;
}

12.4 声明枚举

declare enum Directions {
  Up,
  Down,
  Left,
  Right
}

12.5 声明命名空间

declare namespace jQuery {
  function ajax(url: string, settings?: any): void;
  namespace fn {
    function extend(object: any): void;
  }
}

12.6 声明模块

declare module "url" {
  export interface Url {
    protocol?: string;
    hostname?: string;
    pathname?: string;
  }
  
  export function parse(urlStr: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url;
}

12.7 全局声明

declare global {
  interface Array<T> {
    myMethod(): void;
  }
}

Array.prototype.myMethod = function() {
  // ...
};

13. 配置

13.1 tsconfig.json

{
  "compilerOptions": {
    /* 基本选项 */
    "target": "es5",                          // 目标 ECMAScript 版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ESNext'
    "module": "commonjs",                     // 模块系统: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'esnext'
    "lib": ["es6", "dom"],                    // 要包含的库文件
    "allowJs": true,                          // 允许编译 JavaScript 文件
    "checkJs": true,                          // 在 .js 文件中报告错误
    "jsx": "react",                           // JSX 代码生成: 'preserve', 'react-native', 'react'
    
    /* 严格类型检查选项 */
    "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",               // 模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./",                          // 基础目录
    "paths": {                                // 模块名到基于 baseUrl 的路径映射
      "@/*": ["src/*"]
    },
    "rootDirs": [                             // 根文件夹列表,其组合内容表示运行时的项目结构
      "src/views",
      "generated/views"
    ],
    "typeRoots": [                            // 要包含的类型声明文件路径列表
      "node_modules/@types"
    ],
    "types": [                                // 要包含的类型声明文件名列表
      "node",
      "jest"
    ],
    
    /* Source Map 选项 */
    "sourceMap": true,                        // 生成相应的 .map 文件
    "inlineSourceMap": false,                 // 生成单个 source maps 文件
    "inlineSources": false,                   // 将代码与 sourcemaps 生成到一个文件中
    
    /* 其他选项 */
    "experimentalDecorators": true,           // 启用实验性的装饰器
    "emitDecoratorMetadata": true,            // 为装饰器提供元数据支持
    "esModuleInterop": true,                  // 允许 default import from non-default modules
    "allowSyntheticDefaultImports": true,     // 允许从没有默认导出的模块进行默认导入
    "resolveJsonModule": true,                // 允许导入 .json 文件
    "declaration": true,                      // 生成相应的 .d.ts 文件
    "declarationMap": true,                   // 为每个 .d.ts 文件生成 sourcemap
    "outDir": "./dist",                       // 输出目录
    "removeComments": true,                   // 删除注释
    "newLine": "lf",                          // 指定换行符
    "noEmit": false                           // 不生成输出文件
  },
  "include": [                                // 要编译的文件
    "src/**/*"
  ],
  "exclude": [                                // 排除的文件
    "node_modules",
    "**/*.spec.ts"
  ],
  "files": [                                  // 要编译的特定文件列表
    "src/core.ts"
  ],
  "references": [                             // 项目引用
    { "path": "./common" }
  ]
}

14. 实用工具类型

// Partial<T> - 使所有属性变为可选
interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}

// Required<T> - 使所有属性变为必需
interface Props {
  a?: number;
  b?: string;
}

const obj: Required<Props> = { a: 5, b: "hello" };

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

// todo.title = "Hello"; // 错误:只读

// Record<K, T> - 构造一个类型,其属性键为 K,属性值为 T
interface PageInfo {
  title: string;
}

type Page = "home" | "about" | "contact";

const pages: Record<Page, PageInfo> = {
  home: { title: "Home" },
  about: { title: "About" },
  contact: { title: "Contact" }
};

// Pick<T, K> - 从 T 中选择一组属性 K
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false
};

// Omit<T, K> - 从 T 中排除一组属性 K
type TodoInfo = Omit<Todo, "completed" | "description">;

const todoInfo: TodoInfo = {
  title: "Pick up kids"
};

// Exclude<T, U> - 从 T 中排除可以赋值给 U 的类型
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"

// Extract<T, U> - 从 T 中提取可以赋值给 U 的类型
type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a"

// NonNullable<T> - 从 T 中排除 null 和 undefined
type T2 = NonNullable<string | number | undefined>; // string | number

// Parameters<T> - 获取函数参数类型
declare function f1(arg: { a: number; b: string }): void;
type T3 = Parameters<typeof f1>; // [{ a: number; b: string }]

// ConstructorParameters<T> - 获取构造函数参数类型
type T4 = ConstructorParameters<ErrorConstructor>; // [string]

// ReturnType<T> - 获取函数返回类型
type T5 = ReturnType<() => string>; // string

// InstanceType<T> - 获取构造函数实例类型
class C {
  x = 0;
  y = 0;
}

type T6 = InstanceType<typeof C>; // C

// ThisParameterType<T> - 提取函数 this 参数类型
function toHex(this: Number) {
  return this.toString(16);
}

type T7 = ThisParameterType<typeof toHex>; // Number

// OmitThisParameter<T> - 移除函数 this 参数
type T8 = OmitThisParameter<typeof toHex>; // () => string

// ThisType<T> - 指定上下文类型
type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>; // 在 methods 中,this 的类型是 D & M
};

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
  let data: object = desc.data || {};
  let methods: object = desc.methods || {};
  return { ...data, ...methods } as D & M;
}

let obj2 = makeObject({
  data: { x: 0, y: 0 },
  methods: {
    moveBy(dx: number, dy: number) {
      this.x += dx; // this 的类型被推断为 { x: number, y: number } & { moveBy: Function }
      this.y += dy;
    }
  }
});

15. 异步编程

// Promise
function fetchData(): Promise<string> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Data fetched successfully");
    }, 1000);
  });
}

// async/await
async function getData(): Promise<void> {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

// 多个 Promise
async function fetchMultiple(): Promise<[string, number]> {
  const [data1, data2] = await Promise.all([
    fetchData(),
    Promise.resolve(42)
  ]);
  return [data1, data2];
}

// Promise 类型工具
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;

async function example() {
  const result = await Promise.resolve(Promise.resolve(42));
  type ResultType = Awaited<typeof result>; // number
}

总结

TypeScript 是 JavaScript 的超集,提供了以下核心特性:

  1. 静态类型系统:在编译时进行类型检查
  2. 类型推断:自动推断变量类型
  3. 接口和类型别名:定义复杂的数据结构
  4. 泛型:创建可重用的组件
  5. 装饰器:添加元数据和修改类行为
  6. 模块系统:更好的代码组织和封装
  7. 高级类型:条件类型、映射类型等
  8. 配置灵活:通过 tsconfig.json 精细控制编译选项
  9. 与现代 JavaScript 兼容:支持 ES6+ 所有特性

TypeScript 通过这些特性提高了代码的可维护性、可读性和开发效率,同时保持了与 JavaScript 的完全兼容性。