I am trying to declare the type of a higher order function that can take at most 1 argument.
I should be able to supply a function to my class that takes either 0 or 1 argument. My current try is this:
type FunctionWithOptionalStringArg = ((x?: string) => void)
class Logger {
logger: FunctionWithOptionalStringArg
constructor(logger: FunctionWithOptionalStringArg){
this.logger = logger;
}
log = (x?: string) => {
this.logger(x)
}
}
const logString = (x: string) => console.log(x)
const logDate = () => console.log(Date.now());
const stringLogger = new Logger(logString) //Error
const dateLogger = new Logger(logDate)
However, declaring the stringLogger
returns an error because my class doesn't accept a function that always takes a string argument.
How should I declare my type?
CodePudding user response:
You really don't want to make the x
parameter optional. Inside your Logger
class you are going to pass a string
argument into the logger
callback.
What you're trying to say is that the logger
callback should feel free to ignore that string
parameter if it wants. And this is automatically supported. So, instead of making the parameter optional, just declare that the function accepts a single string
parameter. A zero-arg function is considered compatible with that also, because it is (usually) safe to call a function with extra arguments because such a function will just ignore them.
See the FAQ entry on extra function arguments for more information.
So that means you should change your code to something like:
type FunctionAcceptingStringArg = ((x: string) => void)
class Logger {
logger: FunctionAcceptingStringArg
constructor(logger: FunctionAcceptingStringArg) {
this.logger = logger;
}
log = (x: string) => {
this.logger(x)
}
}
I also changed log
to require a string
argument. Bad things can happen if you actually call logger()
with undefined
for its argument, since logger
might be (x: string) => console.log(x.toUpperCase())
which would give a runtime error in such cases. It is far more likely that you will always call logger
with a string
argument than it is for you to want an optional argument.
Now you can verify that it works as desired:
const logString = (x: string) => console.log(x.toUpperCase())
const logDate = () => console.log(Date.now());
const logStuff = (x: string, y: number) => console.log(x.toUpperCase() y.toFixed(2))
const stringLogger = new Logger(logString) // okay
const dateLogger = new Logger(logDate) // okay
const nope = new Logger(logStuff); // error!
// -------------------> ~~~~~~~~
// Argument of type '(x: string, y: number) => void' is not assignable to
// parameter of type 'FunctionAcceptingStringArg'
The logString
and logDate
callbacks are accepted, since each of them are safe to call with just a string
argument. But logStuff
is rejected because it requires a second argument that will not be passed in.