Typescript 如何键入运行或模拟另一个类型化方法的函数

Typescript 如何键入运行或模拟另一个类型化方法的函数,typescript,mocking,equality,typescript-generics,typeguards,Typescript,Mocking,Equality,Typescript Generics,Typeguards,我试图为一个相当简单的Typescript案例建立打字,但有些东西没有正确绑定。我有返回类型化反应的动作。在框架中补充动作的是执行者。此函数执行一个操作并返回相应的反应(可能模拟该操作) 但是,我在执行者的模拟逻辑中使用的相等测试和类型谓词(用于检查是否应该拦截和模拟某个操作)似乎没有与执行者本身的类型正确耦合,从而导致编译错误 有人能指出需要哪些额外的泛型绑定来消除我面临的编译问题吗 我创建了一个最小的复制,如下所示。MOCK_PERFORM的声明引发编译错误。“Reaction”可以用与“s

我试图为一个相当简单的Typescript案例建立打字,但有些东西没有正确绑定。我有返回类型化反应的动作。在框架中补充动作的是执行者。此函数执行一个操作并返回相应的反应(可能模拟该操作)

但是,我在执行者的模拟逻辑中使用的相等测试和类型谓词(用于检查是否应该拦截和模拟某个操作)似乎没有与执行者本身的类型正确耦合,从而导致编译错误

有人能指出需要哪些额外的泛型绑定来消除我面临的编译问题吗

我创建了一个最小的复制,如下所示。MOCK_PERFORM的声明引发编译错误。
“Reaction”可以用与“string”无关的任意类型实例化。
,就好像编译器无法利用类型谓词来指示“string”是操作的合法反应类型一样

/** DEFINE ACTION AND PERFORMER */

//Defines act() - a method which produces a Reaction
export interface Action<Reaction> {
  act: () => Reaction | Promise<Reaction>;
}

//Performer takes an action, returns a Reaction, (e.g. by running or mocking act())
type Performer = <Reaction>(action:Action<Reaction>) => Promise<Reaction>;


/** MINIMAL ACTION DEFINITION AND MOCKING SCENARIO */

class ProduceStringAction implements Action<string> {
    constructor(readonly value:string){}
    act() {
        return Promise.resolve(this.value)
    }
}
const expectedAction = new ProduceStringAction("hello");
const mockedReaction = "hello";

/** IDENTITY, TYPE PREDICATE FOR AN ACTION */

export function actionMatches<
  Reaction,
  Expected extends Action<Reaction>,
>(actual: Action<any>, expected: Expected): actual is Expected {
  return (
    actual.constructor === expected.constructor &&
    JSON.stringify(actual) === JSON.stringify(expected)
  );
}

/** EXAMPLE PERFORMERS */

//Act performer is simple - always calls act() to produce a Reaction
const ACT_PERFORM:Performer =  async (action) => await action.act();

//Mock performer tries to intercept a specific action, mock its reaction.
const MOCK_PERFORM:Performer = async (action) => {
    if(actionMatches(action, expectedAction)){
        return mockedReaction
    }
    return await ACT_PERFORM(action)
}

const value = MOCK_PERFORM(new ProduceStringAction("hello"))
/**定义动作和执行者*/
//定义act()-产生反应的方法
导出接口操作{
动作:()=>反应|承诺;
}
//执行者执行一个动作,返回一个反应(例如,通过运行或模仿act())
类型Performer=(action:action)=>Promise;
/**最小动作定义和模拟场景*/
类ProduceStringAction实现操作{
构造函数(只读值:字符串){}
法案(){
返回承诺.resolve(this.value)
}
}
const expectedAction=新产品StringAction(“你好”);
const mockedReaction=“你好”;
/**动作的标识、类型谓词*/
导出函数actionMatches<
反应,
预期的行动,

>(实际:操作

我找到了一个解决方案,该解决方案根据给定操作的反应类型编译并运行预期返回类型

为MOCK_PERFORM的Action参数引入一个显式的Action类型足以阻止编译器陷入一个类型狭窄的陷阱,从而创建一个过于狭窄的反应类型,并阻止允许模拟的反应

证明是
mockedResult
realResult
的类型都被正确地推断为
string
,下面的代码可以在模拟和不模拟的情况下运行,以产生相同的结果

/** DEFINE ACTION AND PERFORMER */

//Defines act() - a method which produces a Reaction
interface Action<Reaction> {
  act: () => Reaction | Promise<Reaction>;
}

//Performer takes an action, returns a Reaction, (e.g. by running or mocking act())
type Performer = <Reaction>(action:Action<Reaction>) => Promise<Reaction>;


/** MINIMAL ACTION DEFINITION AND MOCKING SCENARIO */

class ProduceStringAction implements Action<string> {
    constructor(readonly value:string){}
    act() {
        return Promise.resolve(this.value)
    }
}
const expectedAction = new ProduceStringAction("hello");
const mockedReaction = "hello";

/** IDENTITY, TYPE PREDICATE FOR AN ACTION */

function actionMatches<
  Reaction,
  Expected extends Action<Reaction>,
>(actual: Action<any>, expected: Expected): actual is Expected {
  return (
    actual.constructor === expected.constructor &&
    JSON.stringify(actual) === JSON.stringify(expected)
  );
}

/** EXAMPLE PERFORMERS */

//Act performer is simple - always calls act() to produce a Reaction
const ACT_PERFORM:Performer =  async (action) => await action.act();

//Mock performer tries to intercept a specific action, mock its reaction.
const MOCK_PERFORM:Performer = async (action:Action<any>) => {
    if(actionMatches(action, expectedAction)){
        return mockedReaction
    }
    return await ACT_PERFORM(action)
}

async function testOut(){
  const mockedResult = await MOCK_PERFORM(new ProduceStringAction("hello"))
  const realResult = await ACT_PERFORM(new ProduceStringAction("hello"));
  console.log(`Real '${realResult}' Mocked '${mockedResult}'`)
}

testOut()
/**定义动作和执行者*/
//定义act()-产生反应的方法
界面作用{
动作:()=>反应|承诺;
}
//执行者执行一个动作,返回一个反应(例如,通过运行或模仿act())
类型Performer=(action:action)=>Promise;
/**最小动作定义和模拟场景*/
类ProduceStringAction实现操作{
构造函数(只读值:字符串){}
法案(){
返回承诺.resolve(this.value)
}
}
const expectedAction=新产品StringAction(“你好”);
const mockedReaction=“你好”;
/**动作的标识、类型谓词*/
函数actionMatches<
反应,
预期的行动,

>(实际:动作

实验进一步强调了有一种游离类型。它会生成错误
类型“Reaction”不可分配给类型“Reaction”。存在两种同名的不同类型,但它们是不相关的
。我认为未耦合的类型是通过动作推断出的反应,(执行者声明中定义的泛型反应)和来自mockedAction的推断反应(actionMatches()声明中定义的泛型反应)。在模拟场景中,这些需要在第三个位置进行耦合,但我不知道在何处或如何进行耦合。我的预期是,它们将通过推理进行耦合,但这并没有发生,因此需要在某个地方将耦合提升为显式泛型。从另一个方向进行耦合确实会编译,但代价是使用de错误执行者键入的内容类似于…
const ACT\u PERFORM:(操作:操作)=>Promise
因此,
realResult
any
而不是
string
应该是的。因此,也许从那里开始缩小类型范围,直到
ACT\u PERFORM
的返回类型可以推断为string是解决问题的另一种方法。