You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript中检测类是否应用Mixin并推导其类型?

TypeScript Mixin类型判断与智能提示实现方案

问题背景

需要判断TypeScript类是否应用了特定Mixin,以此缩小类型范围获得自动补全/智能提示,但尝试两种方案均失败:

方案1:Mixin内部命名类

在Mixin内部定义命名类,但该类仅在Mixin调用时创建,外部无法访问,导致instanceof无法识别:

class A { }

let BMixin = (superclass: any) => class B extends superclass {
  bProp: string = "bProp";
  bMethod() {
    console.log("method from B");
  }
};

const C = BMixin(A)
const c = new C;
console.log(c instanceof B) // 错误:找不到名称'B'

方案2:工具函数复制属性

创建接收类返回Mixin的工具函数,但需要手动复制所有方法/属性,且instanceof始终返回false:

class A { }

class B {
  bProp: string = "bProp";
  bMethod() {
    console.log("method from B");
  }
}

function defineMixin (mixinClass: any) {
  return (arg0: any) => class extends arg0 {
    // 需手动复制方法和属性
  }
}

const D = defineMixin(B)(A)
const d = new D;
console.log(d instanceof B) // false

期望效果

  1. 类型守卫后获得Mixin的智能提示:
if (hasMixin(something, BMixin)) {
    // something自动提示BMixin的所有属性和方法
}
  1. 链式Mixin后自动合并所有类型:
const Something = CMixin(BMixin(A))
const something = new Something;
// something自动提示A、B、C的所有属性和方法

现有库局限

ts-mixer库提供hasMixin函数,但会修改基类原型,且存在this/super使用限制。

解决方案

通过给Mixin添加唯一标识+类型守卫函数+类型自动合并实现,不修改原原型链,兼容链式调用:

1. 定义Mixin基础类型与工具函数

// 带唯一标识的Mixin类型
type Mixin<T extends new (...args: any[]) => any> = T & {
  readonly __mixinId: symbol;
};

// 创建带唯一标识的Mixin
function createMixin<T extends new (...args: any[]) => any>(
  mixinClass: T
): Mixin<T> {
  const mixin = mixinClass as Mixin<T>;
  mixin.__mixinId = Symbol(mixin.name);
  return mixin;
}

// 应用Mixin并合并类型
function applyMixin<
  Base extends new (...args: any[]) => any,
  Mixin extends new (...args: any[]) => any
>(base: Base, mixin: Mixin) {
  class Mixed extends base {
    constructor(...args: any[]) {
      super(...args);
      // 复制Mixin实例属性
      Object.assign(this, new mixin());
    }
  }
  // 复制Mixin原型方法与静态属性
  Object.assign(Mixed.prototype, mixin.prototype);
  Object.assign(Mixed, mixin);
  
  // 类型层面合并基础类与Mixin的实例类型
  return Mixed as typeof Mixed & (new (...args: ConstructorParameters<Base>) => InstanceType<Base> & InstanceType<Mixin>);
}

2. 实现类型守卫函数

// 判断实例是否应用了指定Mixin的类型守卫
function hasMixin<T extends Mixin<any>>(instance: unknown, mixin: T): instance is InstanceType<T> {
  let proto = Object.getPrototypeOf(instance as object);
  while (proto) {
    if (proto.constructor?.__mixinId === mixin.__mixinId) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }
  // 兜底检查实例是否包含Mixin的核心属性
  return Object.prototype.hasOwnProperty.call(instance, Object.keys(mixin.prototype)[0]);
}

3. 使用示例

// 基础类
class A {
  aProp = "aProp";
  aMethod() { console.log("A method"); }
}

// 创建带标识的Mixin
class B {
  bProp = "bProp";
  bMethod() { console.log("B method"); }
}
const BMixin = createMixin(B);

class C {
  cProp = "cProp";
  cMethod() { console.log("C method"); }
}
const CMixin = createMixin(C);

// 链式应用Mixin
const Something = applyMixin(applyMixin(A, BMixin), CMixin);
const something = new Something();

// 自动提示所有属性和方法
something.aMethod();
something.bMethod();
something.cMethod();

// 类型守卫示例
function test(obj: unknown) {
  if (hasMixin(obj, BMixin)) {
    console.log(obj.bProp); // 自动提示bProp
    obj.bMethod(); // 自动提示bMethod
  }
  if (hasMixin(obj, CMixin)) {
    console.log(obj.cProp);
    obj.cMethod();
  }
}

test(something); // 触发两个守卫分支

方案优势

  • 不修改原类原型链,仅创建新子类,避免原型污染
  • 运行时通过唯一symbol标识判断,准确可靠
  • TypeScript自动推断合并后的类型,完美支持智能提示
  • 兼容this与super的正常使用
  • 支持任意链式Mixin组合

内容的提问来源于stack exchange,提问作者GermanJablo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 14:32:08