首页 > 解决方案 > 具有适当签名的通用函数

问题描述

我不知道如何声明函数繁荣的第二个参数进行正确的类型检查,我的意思是如果有人将函数繁荣的第一个参数作为“foo1”发送,那么第二个参数应该只能是:(number) => void。如果“foo2”那么(string) => void.

interface MyFoo {
    foo1: (number) => void;
    foo2: (string) => void;
}

class Bar<Foo> {
    public boom<T extends Foo, K extends keyof MyFoo>(first: K,
        ...args: Parameters<T[K] /* here I don't know how to declare this parameter */){

    }
}

new Bar<MyFoo>().boom("foo1", /* callback with signature: (number) => void */)

标签: javascripttypescriptfunctiongenericsparameters

解决方案


首先,请注意函数类型中的参数名称是必需的,因此必须更改此定义:

interface MyFoo {
    foo1: (x: number) => void; 
    foo2: (x: string) => void;
}

现在,在方法中Bar<Foo>我们只需要一个泛型参数,因为类型会为我们指定。我不完全理解您的用例,因为看起来您可能想要采用可变数量的参数,但这与您需要回调函数的评论不匹配。所以我假设它需要两个参数:一个键名和一个回调函数:Kboom()Fooboom()boom()

class Bar<Foo> {
    public boom<K extends keyof Foo>(first: K, second: Foo[K]) { }
}

然后是你如何使用它:

// okay, the callback is (x: number) => void
new Bar<MyFoo>().boom("foo1", (x: number) => console.log(x.toFixed(2)));

// error, the callback is (x: number) => void but should be (x: string) => void
new Bar<MyFoo>().boom("foo2", (x: number) => console.log(x.toFixed(2))); 

顺便说一句,这不会阻止您使用具有非函数属性的接口:

interface MyFoo { age: number }
new Bar<MyFoo>().boom("age", 40); // okay

如果您要求boom()只处理与类似函数的属性相对应的键,您将需要一些更高级的条件类型

type KeysMatching<T, V> =
    Extract<keyof T, { [K in keyof T]: T[K] extends V ? K : never }[keyof T]>;

class Bar<Foo> {
    public boom<K extends KeysMatching<Foo, Function>>(first: K, second: Foo[K]) { }
}

// okay, the callback is (x: number) => void
new Bar<MyFoo>().boom("foo1", (x: number) => console.log(x.toFixed(2)));

// error, the callback is (x: number) => void but should be (x: string) => void
new Bar<MyFoo>().boom("foo2", (x: number) => console.log(x.toFixed(2))); 

interface MyFoo { age: number }

// error, "age" is not accepted anymore
new Bar<MyFoo>().boom("age", 40); // error

希望对您有所帮助。祝你好运!


推荐阅读