简体   繁体   English

指定`keyof?时会拒绝打字稿泛型?

[英]Typescript generics is rejected when specifying `keyof?

I have a generic method which is from the RXJS library ( here simplified ) : 我有一个来自RXJS库的通用方法( 这里简化 ):

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

I also have an interface which declares my app values. 我还有一个声明我的app值的界面。 ( keys can be added ) 可以添加键

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

And finally I have a Store which applies to the IState interface with actual values, via the generic function wrapper ( the generic function is an alias to the RXJS subject ) 最后我有一个Store ,通过泛型函数包装器实际值应用于IState接口( 泛型函数是RXJS主题的别名

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

})

Ok so lets add 2 method for getting & setting from store : 好吧,让我们添加2个方法来从商店获取和设置:

Setting to store : 设置存储:

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

Usage : set<string>("key1", "3"); 用法: set<string>("key1", "3");

This function works fine and allows me to only use valid keys which belongs to IState . 此函数工作正常,并允许我只使用属于IState有效密钥 No errors here. 这里没有错误。

But looking at the Select method : 但是看看Select方法:

(invocation should be like: ) (调用应该像:)

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

Here is the method : 这是方法:

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

Type 'string | 输入'string | number' cannot be converted to type 'T'. number'无法转换为'T'类型。 Type 'number' is not comparable to type 'T'. 类型'number'与'T'类型不可比。

Question: 题:

Why is that ? 这是为什么 ? If I remove the keyof : 如果我删除keyof

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

Then it does compile , but it's not making any sense , Store is type of Istate and Istate contains keys of Istate 随后,它编译,但它没有做出任何意义, Store的类型为IstateIstate包含的键Istate

Why does it work without keyof and how can I fix my code so that select method will force to select only keys of Istate ? 为什么没有keyof它可以工作,我如何修复我的代码,以便select方法将强制只选择Istate键?

ONLINE DEMO 在线演示

The problem is that k can be any key of IState so there is no way to make sure that Store[k] is compatible with T . 问题是k可以是IState任何键,因此无法确保Store[k]T兼容。 I would change the generic parameter to be the key, and type the result in relation to the field type: 我会将通用参数更改为键,并键入与字段类型相关的结果:

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

Also set can be improved to not have explicit generic parameters, and to ensure the value passed to set is compatible with the field type: 还可以将set为不具有显式泛型参数,并确保传递给set的值与字段类型兼容:

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

set("key1", 3);

Edit 编辑

As mentioned in the comments, you can see the actual inferred types if you hover over the call (at least in vscode): 如评论中所述,如果将鼠标悬停在调用上,则可以看到实际的推断类型(至少在vscode中):

在此输入图像描述

If you want to keep the explicit type parameters, although I do not recommend this as you can easily mismatch the field type and the call type you can use a type assertion through any : 如果你想保留显式类型参数,虽然我不推荐这个,因为你很容易不匹配字段类型和调用类型,你可以通过any使用类型断言:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM