【问题标题】:Infer type for interface field based on other field根据其他字段推断接口字段的类型
【发布时间】:2020-08-30 05:35:00
【问题描述】:

我正在定义一个以接口为参数的通用函数。
这个接口有几个字段,其中一个是泛型​​参数的键。
另一个字段也采用此通用参数的键,但我想强制两者相等,而不需要用户明确指定它们。

这是一个具体而最小的例子:

// Generic interface representing the parameter of the generic function
interface Inte<DataType extends object, K extends keyof DataType = keyof DataType> {
    key: K;
    fn: (val: DataType[K]) => void;
}

// Define the generic function
function test<DataType extends object>(arg: Inte<DataType>);

// Testing interface
interface Base {
    first: string;
    second: number;
}

// Testing function call
test<Base>({
    key: "first",
    fn: (val: number) => {}, // Error on 'fn', here
});

上面的代码在fn的定义行报错,说明参数类型(number)不能赋值给string,因为它确实推断出我的泛型接口的第二个参数K ,就像 string | number 一样,尽管 key 应该可以帮助它找到正确的类型。

整个错误是(不知道为什么它会在那里放一个随机的ReactText。也许是因为我在.tsx 文件中尝试过这个?):

Type '(val: number) => void' is not assignable to type '(val: ReactText) => void'.
  Types of parameters 'val' and 'val' are incompatible.
    Type 'ReactText' is not assignable to type 'number'.
      Type 'string' is not assignable to type 'number'.ts(2322)

我会遇到一个错误,说参数必须是 string 而不是 number。 (如果我改成val: string,则完全没有错误。

这个问题有什么解决办法吗?我知道我可以做 "strictFunctionTypes": false 但这会削弱我的类型检查。

我很确定我可以采取一些措施来可靠地解决这个问题!

顺便说一句,如果它可能以任何方式相关,实际代码发生在 React 使用的上下文中,这意味着我无法更改泛型函数的参数,因为它是一个组件。

【问题讨论】:

    标签: reactjs typescript typescript-generics


    【解决方案1】:

    问题在于您对Inte 的定义。你给K一个默认值keyof DataType,并且不要在function test&lt;DataType extends object&gt;(arg: Inte&lt;DataType&gt;)中指定一个固定值,这意味着它会/总是/默认为keyof DataType,即使你用一个固定的key 记住。解决此问题的一种方法是使 test 需要 K 的类型:

    declare function test<DataType extends object, K extends keyof DataType>(arg: Inte<DataType, K>);
    
    // Testing function call
    test<Base, "first">({
        key: "first",
        fn: (val: number) => { },
        /*
        ERR on fn:
        Type '(val: number) => void' is not assignable to type '(val: string) => void'.
          Types of parameters 'val' and 'val' are incompatible.
            Type 'string' is not assignable to type 'number'
        */
    });
    

    现在如果你写(val: string) 就可以了。显然这是不符合人体工程学的,因为您需要写出“第一”两次。但是很难避免这种情况,因为DataType 已经是一种浮动类型参数,例如它更多的是一种约束,而不是可以从输入中得出的东西。不幸的是,在 TS 中,一旦指定了一个通用参数,就必须指定 所有 个参数。

    编辑:部分参数推断的一种常见解决方法是使用柯里化函数:

    function test<DataType extends object>() {
        return function innerTest<K extends keyof DataType>(arg: Inte<DataType, K>) {
            throw new Error("Not yet implemented");
        }
    };
    
    test<Base>()({
        key: "first",
        fn: (val: number) => { },
        // still an err, but changing to `val: string` makes it work
    });
    

    现在test&lt;DataType&gt;() 实质上创建了一个带有约束DataType 的测试器函数。返回的函数是对K 的推断。

    【讨论】:

    • 我想知道是否可以通过不必明确指定 "string" 来找到解决方案。我有点难过,我将不得不禁用严格的功能检查来解决这个错误。
    • @Telokis 如果您希望避免这种情况,我已经添加了一个潜在的解决方法。
    • 这可能是一个不错的解决方案,但我无法更改该功能,因为它是一个 React 功能组件,很遗憾:( 不过感谢您的建议。我将能够在其他地方使用它跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-15
    • 2012-05-11
    • 1970-01-01
    • 2021-09-21
    • 2023-04-06
    • 2022-10-16
    • 1970-01-01
    相关资源
    最近更新 更多