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