我需要存储多个不同的函数,这些函数在该参数的自变量旁边都有一个参数.

我已经能够让它工作,并一次对一种类型的函数进行类型判断,但我希望能够一次传递多个不同类型的函数,并对该函数的参数进行正确的类型判断.

type WalkOptions = {
    x: number
    speed: number
}

type JumpOptions = {
    y: number
    speed: number
}

type ShootOptions = {
    type: string
    duration: number
}

const walk = (options: WalkOptions)=>{}
const jump = (options: JumpOptions)=>{}
const shoot = (options: ShootOptions)=>{}

type Action<T extends (options: any) => void> = {
    command: T
    args: Parameters<T>[0]
}

function add<T extends (options: any) => void>(...actions: Action<T>[]){}

使用上面的代码,我可以做add({command: walk, args:{x: 10, speed: 1}}),我会得到正确的类型判断args.我也可以做add({command: walk, args:{x: 10, speed: 1}}, {command: walk, args:{x: -10, speed: 2}}),一切都很好.

然而,如果我做add({command: walk, args:{x: 10, speed: 1}}, {command: jump, args:{x: 5, speed: 1}}),我会在command: jump上得到一个类型错误,因为它应该是(options: WalkOptions) => void.

有没有什么我可以做的,这样我就可以像这样使用Add,并且仍然可以对与它一起使用的命令的参数进行类型判断.

add({
    command: walk,
    args:{
        x: 10,
        speed: 1
    }
},
{
    command: jump,
    args:{
        y: 5,
        speed: 1
    }
},
{
    command: shoot,
    args:{
        type: "laser",
        duration: 3
    }
},
{
    command: walk,
    args:{
        x: -10,
        speed: 2
    }
})

另外,是否可以使用类型判断来创建这些操作的数组,以便在Add Like add(...actionArray)上使用

推荐答案

TypeScrip不希望推断Tgeneric类型参数的值为union type,这在您的情况下实际上很好,因为联合的Action<T>将最终将参数混合在一起.你不会希望walk接受JumpOptions,反之亦然.

你可以让函数在tuple type个类型参数中泛型,而不是在每个element typeAction类型参数中泛型. 然后你可以用mapped tuple type把它转换成actions的类型:

function add<T extends ((options: any) => void)[]>(
  ...actions: { [I in keyof T]: Action<T[I]> }
) { }

所以不是TA | B | C,然后actionsAction<A | B | C>类型,而是T[A, B, C],然后actions[Action<A>, Action<B>, Action<C>]类型.

让我们来测试一下:

add(
  { command: walk, args: { x: 10, speed: 1 } },
  { command: jump, args: { y: 5, speed: 1 } },
  { command: shoot, args: { type: "laser", duration: 3 } },
  { command: walk, args: { x: -10, speed: 2 } }
); // okay
/* T inferred as [
  (options: WalkOptions) => void, 
  (options: JumpOptions) => void, 
  (options: ShootOptions) => void, 
  (options: WalkOptions) => void
]> */

看上go 不错.如果你犯了一个错误,它将被抓住:

add(
  { command: walk, args: { x: 10, speed: 1 } },
  { command: shoot, args: { y: 5, speed: 1 } }, // error!
  // ---------------------> ~  
  // Object literal may only specify known properties, 
  // and 'y' does not exist in type 'ShootOptions'.
  { command: shoot, args: { type: "laser", duration: 3 } },
  { command: walk, args: { x: -10, speed: 2 } }
);
/* T inferred as [
  (options: WalkOptions) => void,
  (options: ShootOptions) => void, 
  (options: ShootOptions) => void, 
  (options: WalkOptions) => void
] */

Playground link to code

Typescript相关问答推荐

如何在同一页面中使用布局和提供程序?

如何根据数据类型动态注入组件?

在这种情况下,如何获得类型安全函数的返回值?

在动态对话框中使用STEP组件实现布线

APIslice-CreateAPI的RTK打字错误

类型,其中一条记录为字符串,其他记录为指定的

在分配给类型的只读变量中维护const的类型

路由链接始终返回到

正交摄影机正在将渲染的场景切成两半

在React中定义props 的不同方式.FunctionalComponent

跟踪深度路径时按条件提取嵌套类型

如何按属性或数字数组汇总对象数组

缩小对象具有某一类型的任意字段的范围

如何连接属性名称和值?

如何使用TypeScrip在EXPO路由链路组件中使用动态路由?

顶点堆叠的图表条形图未在正确的x轴上显示

Typescript:是否将联合类型传递给重载函数?

如何从联合类型推断函数参数?

如何将 MUI 主题对象的自定义属性与情感样式组件中的自定义props 一起使用?

如何在由函数参数推断的记录类型中具有多个对象字段类型