推断通用 'this' 类型
Inferring generic 'this' type
从 question, I am now trying to create functions with an explicit this parameter 类型扩展接口 IModel
开始:
// interface for Model class
interface IModel {
state: {}
}
// based on answer to this question
// now functions will be bound to model instance, so need to specify 'this' parameter as Model
type SingleArgFunction<Model extends IModel, A> = (this: Model, x: A) => A;
type ArrayedReturnFunction<Model extends IModel, A> = (this: Model, x: A) => A[];
type SingleArgFunctionObject<Model extends IModel, AS extends object> = {
[K in keyof AS]: SingleArgFunction<Model, AS[K]>
}
type ArrayedReturnFunctionObject<Model extends IModel, AS extends object> = {
[K in keyof AS]: ArrayedReturnFunction<Model, AS[K]>
}
function makeArrayed<Model extends IModel, A>(f: SingleArgFunction<Model, A>): ArrayedReturnFunction<Model, A> {
return function (x) {
return [f.call(this, x)];
}
}
function makeArrayedAll<Model extends IModel, AS extends object>(
fs: SingleArgFunctionObject<Model, AS>
): ArrayedReturnFunctionObject<Model, AS> {
const result = {} as ArrayedReturnFunctionObject<Model, AS>;
(Object.keys(fs) as (keyof AS)[]).forEach(function<K extends keyof AS>(key: K) {
result[key] = makeArrayed(fs[key]);
})
return result;
}
示例模型和类型定义:
interface MyModel extends IModel {
state: {
x: number;
}
}
interface SingleArgFunctions {
foo: SingleArgFunction<MyModel, number>
}
interface ArrayedReturnFunctions {
foo: ArrayedReturnFunction<MyModel, number>;
}
直接创建ArrayedReturnFunctions
对象即可,this
被推断为类型MyModel
:
const arrayedReturnFunctions1: ArrayedReturnFunctions = {
foo(x) {
return [x + this.state.x]; // ok
}
}
或者,通过将 makeArrayed
应用于单个函数来创建对象也可以:
const arrayedReturnFunctions2: ArrayedReturnFunctions = {
foo: makeArrayed(function (x) {
return x + this.state.x; // ok
})
}
但是,使用 makeArrayedAll
不起作用 - this
被推断为类型 IModel
:
const arrayedReturnFunctions3: ArrayedReturnFunctions = makeArrayedAll({
foo(x) {
return x + this.state.x; // error - property x does not exist on type {}
}
})
即使创建类型为 SingleArgFunctions
的对象然后将其传递给 makeArrayedAll
也不起作用:
const singleArgFunctions: SingleArgFunctions = {
foo(x) {
return this.state.x + x;
}
}
const arrayedReturnFunctions4 = makeArrayedAll(singleArgFunctions); // error - IModel is not assignable to type MyModel
为什么在使用 makeArrayedAll
时 Model
类型没有被推断为 MyModel
?
对我来说,这看起来像是您希望分配给 makeArrayedAll()
输出的变量 arrayedReturnFunctions
的类型将为 [=12] 的输入提供足够的上下文类型=] 以便推断输入法的 this
上下文......但它没有发生。在这种情况下,上下文推理不会发生,我并不感到惊讶,但对于究竟为什么或如何强制它发生,我没有很好的答案。
现在我唯一的建议是注意类型推断在 "forward" 方向上会更好;也就是说,泛型函数的类型参数更容易从函数的 输入 推断出来,而不是从函数的 预期输出类型 推断出来。如果失败,您始终可以手动指定泛型类型参数。
这是一种手动指定模型类型参数并让编译器推断其余部分的方法,使用 currying:
const makeArrayedAllFor = <M extends IModel>() => <AS extends object>(
fs: SingleArgFunctionObject<M, AS>) => makeArrayedAll(fs);
const arrayedReturnFunctions3: ArrayedReturnFunctions = makeArrayedAllFor<MyModel>()({
foo(x) {
return x + this.state.x;
}
})
const arrayedReturnFunctions4 = makeArrayedAllFor<MyModel>()(singleArgFunctions);
虽然很麻烦,但现在可以用了。这是我目前能想到的最好的;也许有人有其他想法?哦,好吧,祝你好运!
从 IModel
开始:
// interface for Model class
interface IModel {
state: {}
}
// based on answer to this question
// now functions will be bound to model instance, so need to specify 'this' parameter as Model
type SingleArgFunction<Model extends IModel, A> = (this: Model, x: A) => A;
type ArrayedReturnFunction<Model extends IModel, A> = (this: Model, x: A) => A[];
type SingleArgFunctionObject<Model extends IModel, AS extends object> = {
[K in keyof AS]: SingleArgFunction<Model, AS[K]>
}
type ArrayedReturnFunctionObject<Model extends IModel, AS extends object> = {
[K in keyof AS]: ArrayedReturnFunction<Model, AS[K]>
}
function makeArrayed<Model extends IModel, A>(f: SingleArgFunction<Model, A>): ArrayedReturnFunction<Model, A> {
return function (x) {
return [f.call(this, x)];
}
}
function makeArrayedAll<Model extends IModel, AS extends object>(
fs: SingleArgFunctionObject<Model, AS>
): ArrayedReturnFunctionObject<Model, AS> {
const result = {} as ArrayedReturnFunctionObject<Model, AS>;
(Object.keys(fs) as (keyof AS)[]).forEach(function<K extends keyof AS>(key: K) {
result[key] = makeArrayed(fs[key]);
})
return result;
}
示例模型和类型定义:
interface MyModel extends IModel {
state: {
x: number;
}
}
interface SingleArgFunctions {
foo: SingleArgFunction<MyModel, number>
}
interface ArrayedReturnFunctions {
foo: ArrayedReturnFunction<MyModel, number>;
}
直接创建ArrayedReturnFunctions
对象即可,this
被推断为类型MyModel
:
const arrayedReturnFunctions1: ArrayedReturnFunctions = {
foo(x) {
return [x + this.state.x]; // ok
}
}
或者,通过将 makeArrayed
应用于单个函数来创建对象也可以:
const arrayedReturnFunctions2: ArrayedReturnFunctions = {
foo: makeArrayed(function (x) {
return x + this.state.x; // ok
})
}
但是,使用 makeArrayedAll
不起作用 - this
被推断为类型 IModel
:
const arrayedReturnFunctions3: ArrayedReturnFunctions = makeArrayedAll({
foo(x) {
return x + this.state.x; // error - property x does not exist on type {}
}
})
即使创建类型为 SingleArgFunctions
的对象然后将其传递给 makeArrayedAll
也不起作用:
const singleArgFunctions: SingleArgFunctions = {
foo(x) {
return this.state.x + x;
}
}
const arrayedReturnFunctions4 = makeArrayedAll(singleArgFunctions); // error - IModel is not assignable to type MyModel
为什么在使用 makeArrayedAll
时 Model
类型没有被推断为 MyModel
?
对我来说,这看起来像是您希望分配给 makeArrayedAll()
输出的变量 arrayedReturnFunctions
的类型将为 [=12] 的输入提供足够的上下文类型=] 以便推断输入法的 this
上下文......但它没有发生。在这种情况下,上下文推理不会发生,我并不感到惊讶,但对于究竟为什么或如何强制它发生,我没有很好的答案。
现在我唯一的建议是注意类型推断在 "forward" 方向上会更好;也就是说,泛型函数的类型参数更容易从函数的 输入 推断出来,而不是从函数的 预期输出类型 推断出来。如果失败,您始终可以手动指定泛型类型参数。
这是一种手动指定模型类型参数并让编译器推断其余部分的方法,使用 currying:
const makeArrayedAllFor = <M extends IModel>() => <AS extends object>(
fs: SingleArgFunctionObject<M, AS>) => makeArrayedAll(fs);
const arrayedReturnFunctions3: ArrayedReturnFunctions = makeArrayedAllFor<MyModel>()({
foo(x) {
return x + this.state.x;
}
})
const arrayedReturnFunctions4 = makeArrayedAllFor<MyModel>()(singleArgFunctions);
虽然很麻烦,但现在可以用了。这是我目前能想到的最好的;也许有人有其他想法?哦,好吧,祝你好运!