如何根据其他参数类型拥有不同的回调签名



我现在正在玩typescript,很难理解如何重载函数,也就是接受回调。

我的函数是一个围绕画布元素的包装器。它最多应接受2个参数:

  1. 回调,接受呈现上下文
  2. 一个可选对象,您可以在其中定义要返回的上下文。它有一个type和一个基于canvas的getContext方法的attributes属性

现在,根据type属性的值,我希望回调签名有所不同。如果该值为"2d"或被省略,则应为(context: CanvasRenderingContext2D) => void,如果该值是"webgl2",则应是(context: WebGL2RenderingContext) => void,依此类推

现在的问题是,我不想在实际实现中使用(context: any) => void作为回调签名(我的linter禁止这样做)。我需要如何定义这个签名,才能获得我想要的结果?

你可以在下面看到我的尝试,但它给了我错误:

重载签名与函数实现不兼容。

function useCanvas(
draw: (context: WebGLRenderingContext) => void,
options: { type: 'webgl' | 'experimental-webgl'; attributes?: WebGLContextAttributes },
): React.RefObject<HTMLCanvasElement>;
function useCanvas(
draw: (context: WebGL2RenderingContext) => void,
options: { type: 'webgl2'; attributes?: WebGLContextAttributes },
): React.RefObject<HTMLCanvasElement>;
function useCanvas(
draw: (context: CanvasRenderingContext2D) => void,
options?: { type?: '2d'; attributes?: CanvasRenderingContext2DSettings },
): React.RefObject<HTMLCanvasElement>;
function useCanvas(
draw: (
context: CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext,
) => void,
{
type = '2d',
attributes,
}: {
type?: '2d' | 'webgl' | 'experimental-webgl' | 'webgl2';
attributes?: CanvasRenderingContext2DSettings | WebGLContextAttributes;
} = {},
): React.RefObject<HTMLCanvasElement> {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
if (canvasRef.current !== null) {
ctx = canvasRef.current.getContext(type, attributes);
draw(ctx);
}
});
return canvasRef;
}

这个想法是,当你写回调参数时,typescript可能会干扰它。这样就不可能在2D上下文中使用方法,而WebGL上只存在这种方法,反之亦然。

非常感谢您的帮助!

这里的问题是TypeScript没有将函数的并集统一为参数的并集函数。

标准库中getContext的定义没有区分不同类型的另一个问题,我们可以通过扩展该接口来解决:

interface HTMLCanvasElement {
getContext(
contextId: '2d', attributes?: CanvasRenderingContext2DSettings
): CanvasRenderingContext2D | null
getContext(
contextId: 'webgl' | 'experimental-webgl', attributes?: WebGLContextAttributes
): WebGLRenderingContext | null
getContext(
contextId: 'webgl2', attributes?: WebGLContextAttributes
): WebGL2RenderingContext | null
}

现在的问题是,我们需要将传入的参数与正确的属性和回调进行匹配。你可以使用元组的并集作为你的参数类型,但要做到这一点,你需要首先,始终通过它们的索引引用单个参数,而不是析构函数(TS的人正在讨论解决这个问题),其次,你必须在顶层有鉴别器(即类型):

function useCanvas(
...args:
[
'2d',
(context: CanvasRenderingContext2D) => void,
CanvasRenderingContext2DSettings?
] |
[
'webgl' | 'experimental-webgl',
(context: WebGLRenderingContext) => void,
WebGLContextAttributes?
] |
[
'webgl2',
(context: WebGL2RenderingContext) => void,
WebGLContextAttributes?
]
): React.RefObject<HTMLCanvasElement> {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
if (canvasRef.current !== null) {
switch (args[0]) {
case 'webgl': // fall-through
case 'experimental-webgl': {
const type = args[0];
const ctx = canvasRef.current.getContext(type, args[2]);
ctx && args[1](ctx);
break;
}
case 'webgl2': {
const ctx = canvasRef.current.getContext(args[0], args[2]);
ctx && args[1](ctx);
break;
}
default: {
const ctx = canvasRef.current.getContext('2d', args[2]);
ctx && args[1](ctx);
break;
}
}
}
});
return canvasRef;
}

最新更新