类初始化器中的自引用泛型

问题描述 投票:0回答:1

考虑这个类的定义:

class MyClass<T extends Record<string, unknown> = any> {
  constructor(
    public params: {
      methodOne: (data: Record<string, unknown>) => T;
      methodTwo: (data: T) => void;
    }
  ) {
    // ...
  }
}
// OK
const myClassInstance = new MyClass({
  methodOne: (data) => {
    return {
      name: "foo",
      lastName: "bar",
    };
  },
  methodTwo: (data) => {
    console.log(data.lastName); //ok
    console.log(data.name); //ok
  },
})

所以我想做的是推断

data
中的
methodTwo
参数(来自
methodOne
,并且在实例化类时创建参数
inline
时,这在这种设置中可以正常工作)实例。

现在我想做同样的事情,但不是直接在

methodTwo
实例的 params 参数中声明
MyClass
,我想将其放在代码中的其他位置,这就是我收到可怕的 (7022) 错误的地方

// NOT OK
//'myClassInstanceTwo' implicitly has type 'any' because it does not have a type annotation 
//and is referenced directly or indirectly in its own initializer.(7022)
const myClassInstanceTwo = new MyClass({
  methodOne: (data) => {
    return {
      name: "foo",
      lastName: "bar",
    };
  },
  methodTwo: methodTwoExternal,
});

type MethodOneReturn<T extends MyClass> = ReturnType<T["params"]["methodOne"]>;
// 'data' is referenced directly or indirectly in its own type annotation.(2502)
function methodTwoExternal(data: MethodOneReturn<typeof myClassInstanceTwo>) {
  console.log(data.lastName); //ok
  console.log(data.name); //ok
}

有办法让这个工作成功吗? 我愿意更改

MyClass
构造函数签名,甚至可能为类实例化引入
factory
函数。

TS游乐场

typescript typescript-generics
1个回答
0
投票

我在这里能想到的(假设这个模式是我们需要支持的)就是制作某种 builder ,让你首先设置

methodOne
,然后得到一个中间的东西,让你提取类型
T 
,然后让你设置
methodTwo
,最后给你一个
MyClass<T>
实例。比如:

class MyClass<T extends Record<string, unknown> = any> {    
  static builder = {
    m1<T extends Record<string, unknown>>(
      methodOne: (data: Record<string, unknown>) => T) {
      return {
        __type: null! as T,
        m2(methodTwo: (data: T) => void) {
          return new MyClass({ methodOne, methodTwo })
        }
      }
    }
  }
  constructor(public params: {
    methodOne: (data: Record<string, unknown>) => T
    methodTwo: (data: T) => void
  }) { }    
}

所以

MyClass.builder.m1(method1).m2(method2)
new MyClass(method1, method2)
相同,但现在您可以在
MyClass.builder.m1(method1)
处暂停并检查它是否有您关心的类型。为了方便起见,我添加了一个虚拟/幻像
__type
属性,这样类型就像
typeof xxx.__type
而不是像
Parameters<Parameters<typeof xxx.m2>[0]>[0]
这样可怕的东西。


让我们看看它是否有效:

const myClassInstanceTwoBuilder =
  MyClass.builder.m1((data) => {
    return {
      name: 'foo',
      lastName: 'bar'
    }
  });

function methodTwoExternal(data: typeof myClassInstanceTwoBuilder.__type) {
  console.log(data.lastName) //ok
  console.log(data.name) //ok
}

const myClassInstanceTwo =
  myClassInstanceTwoBuilder.m2(methodTwoExternal);

看起来不错。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.