【问题标题】:Typescript generics is rejected when specifying `keyof?指定 `keyof? 时拒绝打字稿泛型?
【发布时间】:2018-03-30 19:13:09
【问题描述】:

我有一个来自 RXJS 库的通用方法(这里是简化的):

function Subject<T>(t: T):T {
    return t;
}

我还有一个接口来声明我的应用程序值。 (可以添加键

interface IState { 
    key1: number;
    key2: string;
}

最后我有一个Store,它通过泛型函数包装器应用于具有实际值的IState 接口(泛型函数是RXJS 主题的别名

let Store  : IState=  Subject<IState>({  
    key1: void 0,
    key2: void 0,

})

好的,让我们添加 2 种从商店获取和设置的方法:

设置存储:

 function set<T>(name: keyof IState, statePart: T) {
        Store={
            ...Store,
            [name]: statePart 
        };

用法:set&lt;string&gt;("key1", "3");

此功能运行良好,允许我只使用属于IState的有效密钥。这里没有错误。

但是看看Select 方法:

(调用应该是这样的:)

let myVal:number = select<number>("key1");

方法如下:

function select<T>(k: keyof IState): T { 
    return <T>Store[k];  // <-- error here
}

键入'字符串 | number' 不能转换为类型 'T'。类型 'number' 不能与类型 'T' 比较。

问题:

这是为什么呢?如果我删除 keyof

function select<T>(k): T { 
    return <T>Store[k];
}

然后它会编译,但它没有任何意义,StoreIstate 的类型,Istate 包含 Istate 的键

为什么在没有 keyof 的情况下它可以工作,我该如何修复我的代码,以便 select 方法将强制只选择 Istate 的键?

ONLINE DEMO

【问题讨论】:

    标签: javascript typescript


    【解决方案1】:

    问题在于k 可以是IState 的任意键,因此无法确保Store[k]T 兼容。我会将泛型参数更改为键,并输入与字段类型相关的结果:

    function select<K extends keyof IState>(k: K): IState[K] { 
        return Store[k];
    }
    let myVal  = select("key1"); // myval is number
    

    还可以改进set 使其不具有显式泛型参数,并确保传递给set 的值与字段类型兼容:

    function set<K extends keyof IState>(name: K, statePart: IState[K]) {
        Store={
            ...Store,
            [name]: statePart 
        }
    }
    
    set("key1", 3);
    

    编辑

    如 cmets 中所述,如果将鼠标悬停在调用上(至少在 vscode 中),您可以看到实际推断的类型:

    如果你想保留显式类型参数,虽然我不推荐这样做,因为你很容易使字段类型和调用类型不匹配,你可以通过any使用类型断言:

    function select3<T>(k: keyof IState): T { 
        return Store[k] as any;
    }
    

    【讨论】:

    • 谢谢 — 但现在,作为一名程序员,我发现很难看到实际的类型,而不是之前我将其视为 select&lt;string&gt;(..).... now i know and expect it to be a string... 的情况。
    • @RoyiNamir 是的,但在您的情况下,您实际上不是类型安全的,因为对于 set,例如您可能会不小心指定错误的类型(正如您在问题 key1 中实际所做的那样是数字但是您将string 传递给set。如果将鼠标悬停在IDE 中的调用上,您将看到为调用推断出的实际类型。有一个解决方案可以使您的代码正常工作,但它涉及类型断言而不是类型安全。为了完整起见,我会将其添加到答案中。
    • 感谢 Titian - 我什至没有注意到在我的示例中它没有类型安全;)
    • @RoyiNamir 在我输入函数之前我也没有,这样的错误很容易犯,最好让编译器尽可能多地推断,并在检测到不兼容时给出错误
    • 顺便说一句,为什么编译器对 function select&lt;T&gt;(k): T { return &lt;T&gt;Store[k]; } 很满意? ,最糟糕的是它允许我这个select&lt;number&gt;("key2") //key2 是一个字符串`
    猜你喜欢
    • 2022-08-21
    • 1970-01-01
    • 2022-01-18
    • 2020-09-06
    • 2023-02-16
    • 2015-11-27
    • 2020-08-13
    • 2021-11-26
    • 1970-01-01
    相关资源
    最近更新 更多