Skip to content

Ts实现一个模式匹配

Posted on:2025年1月13日 at 22:29

前言

众所不周知,在 Rust 语言中,有一个强大的语法,模式匹配:

fn main() {
    let data = Some(12);
    match data {
        Some(i) => println!("{}", i),
        None => println!("No data"),
    }
}

那么在 typescript 中我们如何为自己实现一个这么好用的语法呢?

match.ts

export type MatchModel<T, R> = Array<
  [((value: T) => boolean) | T, (value: T) => R]
>;

// 定义 Match 类来处理模式匹配
class Match<T> {
  private readonly value: T;

  constructor(value: T) {
    this.value = value;
  }

  // with 方法用于定义匹配分支
  with<R>(patterns: Array<[((value: T) => boolean) | T, (value: T) => R]>): R {
    for (const [pattern, handler] of patterns) {
      if (
        typeof pattern === "function"
          ? (pattern as (value: T) => boolean)(this.value)
          : pattern === this.value
      ) {
        return handler(this.value);
      }
    }
    throw new Error("No pattern matched");
  }
}

// 创建 match 函数作为入口
export function match<T>(value: T): Match<T> {
  return new Match(value);
}

usage1

基础数值匹配

const numberModel: MatchModel<number, string> = [
  [1, n => `one: ${n}`],
  [2, n => `two: ${n}`],
  [3, n => `three: ${n}`],
  [n => n > 3, n => `more than three: ${n}`],
];
console.log(match(1).with(numberModel)); // "one: 1"
console.log(match(100).with(numberModel)); // "more than three: 100"

usage2

自定义类型匹配

type Result<T, E = Error> = {
  error?: E;
  value?: T;
};

type ResultModel<T, E = Error> = [
  (result: Result<T, E>) => boolean,
  (result: Result<T, E>) => string
];
const resultModel: ResultModel<number>[] = [
  [r => !r.error, r => `Success: ${r.value}`],
  [r => !!r.error, r => `Error: ${r.error}`],
];
console.log(match({ value: 42 }).with(resultModel)); //  "Success: 42"
console.log(
  match({ error: new Error("Something went wrong") }).with(resultModel)
); // "Error: Error: Something went wrong"

usage3

枚举匹配

enum Color {
  Red,
  Green,
  Blue,
}
const colorModel: MatchModel<Color, string> = [
  [Color.Red, () => "Found Red"],
  [Color.Green, () => "Found Green"],
  [Color.Blue, () => "Found Blue"],
];
console.log(match(Color.Green).with(colorModel)); // "Found Green"
console.log(match(Color.Blue).with(colorModel)); // "Found Blue"