TypeScript中如何声明可接收0或1个参数的高阶函数类型
报错原因
TypeScript 开启严格函数类型检查时,函数参数遵循逆变规则:若要将函数A赋值给类型B,A的参数类型必须比B对应参数的类型更宽泛,才能保证所有调用场景下都不会出现参数不匹配的问题。
你定义的FunctionWithOptionalStringArg等价于(x: string | undefined) => void,允许调用方传或不传参数。但传入的logString类型为(x: string) => void,它的参数类型是string,比string | undefined更窄,无法处理「调用时不传参、拿到undefined」的场景,因此TS会抛出类型错误,避免运行时异常。
正确实现方案
推荐方案:泛型实现(完全类型安全)
通过泛型自动适配传入函数的参数类型,既支持传入0参数函数,也支持传入1个必选string参数的函数,同时调用log方法时TS会自动做参数校验,不存在类型漏洞:
// 约束泛型只能是无参数,或者仅一个string参数的元组类型 class Logger<TArgs extends [] | [string]> { private logger: (...args: TArgs) => void; constructor(logger: (...args: TArgs) => void) { this.logger = logger; } log = (...args: TArgs) => { this.logger(...args); } } const logString = (x: string) => console.log(x); const logDate = () => console.log(Date.now()); // 自动推导为Logger<[string]>,调用log时必须传入string参数 const stringLogger = new Logger(logString); stringLogger.log("打印日志"); // 正常 stringLogger.log(); // 类型错误:缺少必填参数 // 自动推导为Logger<[]>,调用log时不能传入任何参数 const dateLogger = new Logger(logDate); dateLogger.log(); // 正常 dateLogger.log("测试"); // 类型错误:多余参数
可选方案:保持log方法可选参数(需自行处理参数边界)
如果你希望保持log方法传不传参都可以的调用形式,需要明确区分不同logger的调用逻辑,避免给必传string参数的函数传入undefined。这种写法需要做内部参数判断,否则存在运行时风险:
type LoggerFunction = ((x?: string) => void); class Logger { private logger: LoggerFunction; constructor(logger: LoggerFunction) { this.logger = logger; } log = (x?: string) => { // 有参数时传参调用,无参数时无参调用 return x === undefined ? this.logger() : this.logger(x); } } const logString = (x: string) => console.log(x); const logDate = () => console.log(Date.now()); // 注意:这里需要做类型断言,因为TS仍然会认为logString无法处理无参调用 // 你需要自己保证log方法内部不会给logString传undefined const stringLogger = new Logger(logString as LoggerFunction); const dateLogger = new Logger(logDate); stringLogger.log("打印日志"); dateLogger.log();
注意:非必要不使用类型断言,这种写法绕过了TS的类型检查,如果内部逻辑写错,很容易出现
undefined传给logString的运行时错误。
内容的提问来源于stack exchange,提问作者wasmachien
相关产品推荐
相关产品推荐

