不简单:从函数中返回一个类

1 年前

不就 这样 么?

export function test() {
  class Inner {}
  return Inner;

结果当然是报错了。在这里 test() 没有注解返回值的类型,那么 TypeScript 自然而然会去推测返回值类型,而返回值是函数内部的 不公开 类型 Inner ,TypeScript 无法在函数外部表示函数内部的类型。

那么要解决的核心问题就出现了: 如何避免在函数的返回值中涉及函数内部的类型?

解决方案

匿名类

Typescript 会将匿名类的类型表示成一个又臭又长的 {} 匿名类型,因不涉及函数内部的类型了,自然就不报错了:

export function test() {
  return class Anonymous {
    method() {}

优点

  • 代码一目了然,写起来方便
  • 适用大部分简单情况

缺点

  • 无法声明私有成员、保护乘员
  • 无法使用装饰器

断言返回值类型

通过将返回值从自动推测出的内部类型 Inner 断言至一个等价的匿名 {} 类型,就可以完美避免在返回值类型中出现函数内部的类型:

export function test() {
  class Inner {
    static p = 3;
    constructor(private property: string) {}
    method() {}
  type InstanceType = {
    [K in keyof Inner]: Inner[K];
  type StaticMembers = {
    [K in Exclude<keyof typeof Inner, "prototype">]: typeof Inner[K];
  } & { prototype: InstanceType };
  return Inner as {
    new (...args: ConstructorParameters<typeof Inner>): InstanceType;
  } & StaticMembers;

优点

  • 支持私有成员、保护成员、装饰器

缺点

  • 代码繁琐,不易理解
  • 子类无法访问保护成员

注解返回值类型

通过使用外部类型标注函数的返回值来避免涉及函数内部类型:

export interface Instance {
  method(): void;
export interface Constructor {
  new (property: string): Instance;
  prototype: Instance;
  p: number;
export function test(): Constructor {
  class Inner {
    static p = 3;
    constructor(private property: string) {}
    method() {}
  return Inner;

优点

  • 代码清晰、易于理解
  • 支持私有成员、保护成员、装饰器

缺点

  • 代码大量重复
  • 子类无法访问保护成员

拓展:Mixins

在翻某库的代码时,发现了 这么一种很玄学的方法

export function test<T extends { new (...args: any[]): any }>(baseType: T) {
  class Inner extends baseType {
    static staticProperty = "aaaa";
    constructor(...args: any[]) {
      super();