考虑这个类的定义:
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
函数。
我在这里能想到的(假设这个模式是我们需要支持的)就是制作某种 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);
看起来不错。