此刻我在玩打字稿,在掌握如何重载接受回调的函数时遇到困难。
我的功能是围绕canvas元素进行包装。它最多可以接受2个参数:
type
和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;
}
这个想法是,打字稿在编写时可能会干扰回调参数。这样就不可能在2D上下文中使用WebGL上存在的方法,反之亦然。
我们非常感谢您的帮助!
答案 0 :(得分:1)
这里的问题是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;
}