TypeScript:调用签名示例问题

huangapple go评论59阅读模式
英文:

typescript: call signatures example question

问题

以下是翻译好的内容:

我正在学习 TypeScript 中的调用签名,需要 TypeScript 专家的帮助:

以下代码可以编译,但在 'typescript playground' 中运行时会导致运行时错误。
运行时错误为:([ERR]logFunc 不是一个函数)

type LogFunctionWithPrefixCB = {
    logPrefix: string;
    (stringToLog: string): void; // 这是快捷方式 - 据说 LogFunctionWithPrefixCB 类型是可调用的(可以将其作为函数调用)
}

function makeLogFunctionWithPrefixCB(prefixStr: string): LogFunctionWithPrefixCB {

    let loggerFunc = function (this: LogFunctionWithPrefixCB, stringToLog: string) {
        console.log(`${this.logPrefix}: ${stringToLog}`);
    };

    let ret = {
        logPrefix: prefixStr,
        call: loggerFunc
    };

    return ret as LogFunctionWithPrefixCB;

}

// 使用
function doSomething(logFunc: LogFunctionWithPrefixCB) {
    logFunc("hello world");    // 没有编译错误 - 但会得到运行时错误 "logFunc 不是一个函数"
}

let logger = makeLogFunctionWithPrefixCB("prefix:");
doSomething(logger);

现在下一个示例不能编译,但运行后会得到预期结果:(参见 logfunc.call(...)
TypeScript 编译错误为:"Expected 2 arguments, but got 1."

type LogFunctionWithPrefixCB = {
    logPrefix: string;
    (stringToLog: string): void; // 这是快捷方式 - 据说 LogFunctionWithPrefixCB 类型是可调用的(可以将其作为函数调用)
}

function makeLogFunctionWithPrefixCB(prefixStr: string): LogFunctionWithPrefixCB {

    let loggerFunc = function (this: LogFunctionWithPrefixCB, stringToLog: string) {
        console.log(`${this.logPrefix}: ${stringToLog}`);
    };

    let ret = {
        logPrefix: prefixStr,
        call: loggerFunc
    };

    return ret as LogFunctionWithPrefixCB;

}

// 使用
function doSomething(logFunc: LogFunctionWithPrefixCB) {
    logFunc.call("hello world");    // 可以工作,但 TypeScript 编译错误 "Expected 2 arguments, but got 1."
}

let logger = makeLogFunctionWithPrefixCB("prefix:");
doSomething(logger);
英文:

I am learning about typescript call signatures and need the help of a typescript expert:

The following code compiles, but running it in 'typescript playground' results in a runtime error.
The runtime error is: ([ERR]: logFunc is not a function)

    type LogFunctionWithPrefixCB = {
logPrefix: string;
(stringToLog: string) : void; // that's the shortcut - supposedly type  LogFunctionWithPrefixCB is callable (can call it as a function)
}
function makeLogFunctionWithPrefixCB( prefixStr : string) : LogFunctionWithPrefixCB {
let loggerFunc = function(this : LogFunctionWithPrefixCB, stringToLog: string) {
console.log(`${this.logPrefix}: ${stringToLog}`);
};
let ret = {
logPrefix: prefixStr,
call : loggerFunc
};
return ret as LogFunctionWithPrefixCB;
}
// using the
function doSomething(logFunc : LogFunctionWithPrefixCB) {
logFunc("hello world");    // no compilation error - but get runtime error "logFunc is not a function "
}
let logger = makeLogFunctionWithPrefixCB("prefix:");
doSomething(logger);

Now the next example does not compile, but it runs and gives the expected result: (see logfunc.call(...))
The typescript compilation error is: "Expected 2 arguments, but got 1."

Tried lots of typescript versions - all with the same result.

    type LogFunctionWithPrefixCB = { 
logPrefix: string;
(stringToLog: string) : void; // that's the shortcut - supposedly type  LogFunctionWithPrefixCB is callable (can call it as a function)
}
function makeLogFunctionWithPrefixCB( prefixStr : string) : LogFunctionWithPrefixCB {
let loggerFunc = function(this : LogFunctionWithPrefixCB, stringToLog: string) {
console.log(`${this.logPrefix}: ${stringToLog}`);
};
let ret = {
logPrefix: prefixStr,
call : loggerFunc
};
return ret as LogFunctionWithPrefixCB;
}
// using the 
function doSomething(logFunc : LogFunctionWithPrefixCB) {
logFunc.call("hello world");    // works, but typescript compilation error "Expected 2 arguments, but got 1 "
}
let logger = makeLogFunctionWithPrefixCB("prefix:");
doSomething(logger);

答案1

得分: 1

以下是翻译好的内容:

有多种方法可以创建具有属性的函数。最简单的方法是将函数值创建为const,然后随后添加属性,这在TypeScript中是明确支持的。示例代码如下:

const f = function(){ /*⋯*/ };
f.prop = 123;

这是您可以采取的基本方法。但是:

您的函数的形式如下:

const f = function(stringToLog) {
  console.log(`${this.logPrefix}: ${stringToLog}`);
}
f.logPrefix = prefixStr;

但唯一能够起作用的方式是如果函数被调用的this上下文恰好是函数本身。这非常不太可能。如果只是像这样调用它 f(""),那么根本没有this上下文(它几乎肯定会是undefined)。在调用之前,您必须将其bind到自身,像这样 f.bind(f)("")。如果您希望这个过程自动发生,那么您需要定义类似于 const g = f.bind(f)。但是现在g只是一个没有额外logPrefix属性的函数。这很混乱,比必要复杂得多。操纵this可能不是您想要的。

相反,您可以在使用this的地方直接引用函数对象本身。这将我们带到以下实现:

function makeLogFunctionWithPrefixCB(prefixStr: string): LogFunctionWithPrefixCB {
  const loggerFunc = function (stringToLog: string) {
    console.log(`${loggerFunc.logPrefix}: ${stringToLog}`);
  };
  loggerFunc.logPrefix = prefixStr;
  return loggerFunc;
}

这段代码可以编译而不会出错,我们可以看到它的工作方式:

function doSomething(logFunc: LogFunctionWithPrefixCB) {
  logFunc("hello world");
}

let logger = makeLogFunctionWithPrefixCB("prefix");
doSomething(logger); // "prefix: hello world" 
logger.logPrefix = "changedPrefix";
doSomething(logger); // "changedPrefix: hello world" 

看起来不错。logger对象的类型为LogFunctionWithPrefixCB,因此它具有logPrefix属性,如果我们更改该属性,它将按预期方式更改logger函数的行为。

英文:

There are multiple ways to make a function that also has properties on it. The easiest way to do so is to create your function value as a const and then just add the properties afterward, which is explicitly supported in TypeScript. Something like:

const f = function(){ /*⋯*/ };
f.prop = 123;

That is the basic approach you can take. But:


Your function is of the form

const f = function(stringToLog) {
console.log(`${this.logPrefix}: ${stringToLog}`);
}
f.logPrefix = prefixStr;

but the only way that would work is if the this context on which the function is called happens to be the function itself. And this is very, very unlikely. If you just call it like f(""), then there's no this context at all (it would almost certainly be undefined). You'd have to bind it to itself before calling it, like f.bind(f)(""). And if you wanted this to happen automatically, then you'd need to define something like const g = f.bind(f). But now g is just a function without the extra logPrefix property. It's a mess, and much more complicated than necessary. Messing with this is probably not what you want.

Instead you can just refer to the function object directly where you were using this. That brings us to this implementation:

function makeLogFunctionWithPrefixCB(prefixStr: string): LogFunctionWithPrefixCB {
const loggerFunc = function (stringToLog: string) {
console.log(`${loggerFunc.logPrefix}: ${stringToLog}`);
};
loggerFunc.logPrefix = prefixStr;
return loggerFunc;
}

That compiles without error, and we can see how it works:

function doSomething(logFunc: LogFunctionWithPrefixCB) {
logFunc("hello world");
}
let logger = makeLogFunctionWithPrefixCB("prefix");
doSomething(logger); // "prefix: hello world" 
logger.logPrefix = "changedPrefix";
doSomething(logger); // "changedPrefix: hello world" 

Looks good. The logger object is of type LogFunctionWithPrefixCB and so it has a logPrefix property, and if we alter that property it alters the behavior of logger's function in the expected way.

Playground link to code

huangapple
  • 本文由 发表于 2023年7月17日 20:58:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76704727.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定