Typescript 字符串或数字字典类型参数

Typescript 字符串或数字字典类型参数,typescript,Typescript,我有这个功能: interface NumDict<T> { [key : number] : T } export function mapNumDictValues<T,R>(dict: NumDict<T>, f: (v: T, key?: number) => R): NumDict<R> { let emptyDict : NumDict<R> = {}; return Object.keys(dict).

我有这个功能:

interface NumDict<T> {
  [key : number] : T
}

export function mapNumDictValues<T,R>(dict: NumDict<T>, f: (v: T, key?: number) => R): NumDict<R> {
  let emptyDict : NumDict<R> = {};
  return Object.keys(dict).reduce((acc, key) => {
    const keyInt = parseInt(key);
    acc[keyInt] = f(dict[keyInt], keyInt);
    return acc;
  }, emptyDict);
}

有办法吗?

由于javascript如何处理对象键(仅字符串)和索引表达式(字符串/数字/符号/任意)上的typescript限制,您所追求的目标不容易实现(至少我找不到办法)以及类型
number | string
number
/
string
之间的差异

您设法获得的唯一类型安全性(据我所知)是,您在一个案例中使用
noImplicitAny
(模糊错误)得到了一个错误,但您仍然受限于使用此词典所能做的事情

我对你的情况不太了解,但听起来你在这里几乎没有什么收获,以获得体面的解决方案,除非你真的需要这种类型的安全性,那么我认为最好把你的钥匙当作字符串来处理,然后结束它,但如果您确实需要它,我建议您创建自己的字典类型实现来处理一些事情,例如:

interface Dict<K extends number | string, V> {
    get(key: K): V;
    set(key: K, value: V): void;
    mapValues<R>(f: (v: V, key?: K) => R): Dict<K, R>;
}

abstract class BaseDict<K extends number | string, V> implements Dict<K, V> {
    protected items: { [key: string]: V } = Object.create(null);

    get(key: K): V {
        return this.items[key.toString()];
    }

    set(key: K, value: V): void {
        this.items[key.toString()] = value;
    }

    abstract keys(): K[];

    values(): V[] {
        return Object.keys(this.items).map(key => this.items[key]);
        // or Object.values(this.items) with ES6
    }

    mapValues<R>(fn: (v: V, key?: K) => R): Dict<K, R> {
        let dict: Dict<K, R> = Object.create(this.constructor.prototype);
        this.keys().forEach(key => dict.set(key, fn(this.get(key), key)));
        return dict;
    }
}

class NumDict<V> extends BaseDict<number, V> {
    keys(): number[] {
        return Object.keys(this.items).map(key => parseFloat(key));
    }
}

class StringDict<V> extends BaseDict<string, V> {
    keys(): string[] {
        return Object.keys(this.items);
    }
}
接口指令{
get(key:K):V;
设置(键:K,值:V):无效;
映射值(f:(v:v,键?:K)=>R):Dict;
}
抽象类BaseDict实现Dict{
受保护项:{[key:string]:V}=Object.create(null);
获取(键:K):V{
返回此.items[key.toString()];
}
集合(键:K,值:V):无效{
this.items[key.toString()]=值;
}
抽象键():K[];
值():V[]{
返回Object.keys(this.items).map(key=>this.items[key]);
//或使用ES6的Object.values(this.items)
}
映射值(fn:(v:v,键?:K)=>R):Dict{
让dict:dict=Object.create(this.constructor.prototype);
this.keys().forEach(key=>dict.set(key,fn(this.get(key,key)));
返回命令;
}
}
类NumDict扩展了BaseDict{
keys():编号[]{
返回Object.keys(this.items).map(key=>parseFloat(key));
}
}
类StringDict扩展了BaseDict{
keys():字符串[]{
返回Object.key(此.items);
}
}
您需要使用CTOR创建DICT,这不如使用
{}
舒适,但您确实有更多的控制权,例如,请注意,在
BaseDict.mapValues
中调用map函数(
fn
)时,键将具有正确的类型(如果为
NumDict
则为数字,如果为
StringDict
则为字符串)。

请尝试以下操作:

interface IStringToNumberDictionary {
    [index: string]: number;
}


interface INumberToStringDictionary {
    [index: number]: string;
}

type IDictionary = IStringToNumberDictionary | INumberToStringDictionary;
例如:

let dict: IDictionary = Object.assign({ 0: 'first' }, { 'first': 0 });
let numberValue = dict["first"]; // 0
let stringValue = dict[0]; // first
在您的情况下,类似这样的情况:

interface IStringKeyDictionary<T> {
    [index: string]: T;
}


interface INumberKeyDictionary<T> {
    [index: number]: T;
}

type IDictionary<T> = IStringKeyDictionary<T> | INumberKeyDictionary<T>;
接口IStringKeyDictionary{
[索引:字符串]:T;
}
MBerkeydictionary中的接口{
[索引:编号]:T;
}
IDictionary类型=IStringKeyDictionary | INumberKeyDictionary;

string | number
不是
字符串
也不是
数字
它是它自己的一种类型,您需要将它转换为其中一种:
{[id:K as string]:R}
。另外,要考虑到javascript中没有数字键,如果这样做:
a[1]=3
它将执行
a[“1”]=3
我选中了,您不能在那里强制转换,但正如我已经说过的,js中的字典键总是字符串,所以只需执行:
{[id:string]:R}
而且您可能不需要
K
我知道对象/字典键实际上是字符串,但在类型级别上,Typescript允许您仅对键强制使用数字,我利用数字是为了更大的类型安全性。如果我使用
{[id:string]:R}
我失去了类型安全性,这正是我需要
K
的原因。这:
mapNumDictValues({“key”:“value”},(value,key)=>value.length)
对于编译器来说很好,即使
“key”
不是一个数字。所以我不太清楚你的意思是什么类型的安全。你是指传递的迭代器函数中的键的类型吗?当然,当字典的签名指定键是数字时,键是一个数字。TypeScript允许字典的类型签名中有一个
number
类型的键不是不正确的de>string和
number
可以很好地作为键的输入,正如您在第一篇评论中提到的。当然
对象的返回类型。键
string[]
。谢谢Nitzan,这是一个不错的解决方法。但实际上我想要这个问题的答案,因为我正在使用它们(半-)在我自己的数据结构中键入字典。我宁愿有两个函数
mapNumDict
mapDict
的不便,也不愿有另一层间接寻址的开销。反正我也不打算在我自己的数据结构之外调用这些函数。哇,我不知道我当时怎么没有看到这一点:)这是正确的答案。除了它必须是
IStringKeyDictionary | INumberKeyDictionary
,所以我编辑了一点。
let dict: IDictionary = Object.assign({ 0: 'first' }, { 'first': 0 });
let numberValue = dict["first"]; // 0
let stringValue = dict[0]; // first
interface IStringKeyDictionary<T> {
    [index: string]: T;
}


interface INumberKeyDictionary<T> {
    [index: number]: T;
}

type IDictionary<T> = IStringKeyDictionary<T> | INumberKeyDictionary<T>;