Typescript 如何根据其他参数类型拥有不同的回调签名?
我现在正在玩typescript,很难掌握如何重载函数,即接受回调 我的函数是画布元素的包装器。它最多应接受2个参数:Typescript 如何根据其他参数类型拥有不同的回调签名?,typescript,Typescript,我现在正在玩typescript,很难掌握如何重载函数,即接受回调 我的函数是画布元素的包装器。它最多应接受2个参数: 接受呈现上下文的回调 一个可选对象,您可以在其中定义要返回的上下文。它有一个类型和一个属性属性,基于canvas的方法 现在,根据type属性的值,我希望回调签名不同。如果值为“2d”或省略,则应为(上下文:CanvasRenderingContext2D)=>void,如果值为“webgl2”,则应为(上下文:WebGL2RenderingContext)=>void,依此类
类型
和一个属性
属性,基于canvas的方法type
属性的值,我希望回调签名不同。如果值为“2d”或省略,则应为(上下文:CanvasRenderingContext2D)=>void
,如果值为“webgl2”,则应为(上下文:WebGL2RenderingContext)=>void
,依此类推
现在的问题是,我不希望在实际实现中将(context:any)=>void
作为回调签名(我的linter禁止这样做)。我需要如何定义此签名才能获得所需的结果
您可以在下面看到我的尝试,但它给出了错误:
重载签名与函数实现不兼容
函数使用画布(
绘制:(上下文:WebGLRenderingContext)=>void,
选项:{type:'webgl'|'experimental webgl';attributes?:WebGLContextAttributes},
):React.reObject;
函数useCanvas(
绘制:(上下文:WebGL2RenderingContext)=>void,
选项:{type:'webgl2';attributes?:WebGLContextAttributes},
):React.reObject;
函数useCanvas(
绘制:(上下文:CanvasRenderingContext2D)=>void,
选项?:{type?:'2d';属性?:CanvasRenderingContext2DSettings},
):React.reObject;
函数useCanvas(
抽签:(
上下文:CanvasRenderingContext2D | WebGLRenderingContext | WebGL2RenderingContext,
)=>无效,
{
类型='2d',
属性,
}: {
类型?:“2d”|“webgl”|“实验webgl”|“webgl2”;
属性?:CanvasRenderingContext2DSettings | WebGLContextAttributes;
} = {},
):React.reObject{
const canvasRef=useRef(null);
useffect(()=>{
if(canvasRef.current!==null){
ctx=canvasRef.current.getContext(类型、属性);
抽签(ctx);
}
});
返回canvasRef;
}
这个想法是,当您编写回调参数时,typescript可能会干扰回调参数。那么就不可能在二维上下文中使用方法,而二维上下文只存在于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;
}
函数使用画布(
…args:
[
“2d”,
(上下文:CanvasRenderingContext2D)=>void,
CanvasRenderingContext2DSettings?
] |
[
“webgl”|“实验性webgl”,
(上下文:WebGLRenderingContext)=>void,
WebGLContextAttributes?
] |
[
“webgl2”,
(上下文:WebGL2RenderingContext)=>void,
WebGLContextAttributes?
]
):React.reObject{
const canvasRef=useRef(null);
useffect(()=>{
if(canvasRef.current!==null){
开关(参数[0]){
案例“webgl”://失败
案例“实验性webgl”:{
常量类型=args[0];
const ctx=canvasRef.current.getContext(类型,args[2]);
ctx&参数[1](ctx);
打破
}
案例“webgl2”:{
const ctx=canvasRef.current.getContext(args[0],args[2]);
ctx&参数[1](ctx);
打破
}
默认值:{
const ctx=canvasRef.current.getContext('2d',args[2]);
ctx&参数[1](ctx);
打破
}
}
}
});
返回canvasRef;
}
啊,这是有道理的。不幸的是,如果我尝试您的解决方案(如果我理解正确,这只是调用签名本身的并集),它仍然会给我一个错误:无法调用类型缺少调用签名的表达式。键入“((上下文:CanvasRenderingContext2D)=>void);((上下文:WebGL2RenderingContext)=>void);((上下文:WebGLRenderingContext)=>void)”没有兼容的调用签名。
@cymin您可以让您的实现使用draw:(上下文:any)=>void
,它不会将上下文:any
作为调用签名公开,然后在您的实现中使用类型保护来安全地确定它是什么类型的上下文(如果您的原始语法实际工作,您也会这样做)。@Aaron我不认为,这正是我想要的。函数的用法应该是useCallback(thisIsA2dContext=>{…})
或useCallback(thisIsA2dContext=>{…},{type:'2d',…)
或useCallback(thisIsAWebGLContext=>{…},{type:'webgl',…)
。因此,通过函数调用,我可以在编译时说,回调的参数将是什么类型。因为我只使用canvas元素的getContext方法,这将返回null或指定的上下文。如果我检查上下文是否不为null,那么我应该能够调用回调,对吗?所以我希望实现的是s、 当调用useCanvas
时,不能意外地传递一个回调函数,该回调函数期望错误的上下文类型并调用在上下文中不可用的方法,即
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;
}