繁体   English   中英

打字稿:通用地图<K,V>值类型

[英]Typescript: Generic over Map<K,V> value type

这是问题的简化版本。 我正在尝试创建一个函数,该函数将在具有Map属性的接口上进行通用操作。

interface Data {
  map1: Map<string, number>;
  map2: Map<string, string>;
}

const d: Data = {
  map1: new Map(),
  map2: new Map(),
};

// This doesn't work
public dataSet<T extends keyof Data, V extends Data[T]... (need help here)>(d: data, dataKey: T, key: string, val: V) {
  d[dataKey].set(key, val);
}

// Ideally, called like:
dataSet(d, 'map1', 'alpha', 3);
dataSet(d, 'map2', 'beta', 'charlie');

V的类型定义目前是Map类型而不是Map的值类型。

使用对象而不是 Maps 怎么样?

interface Data {
  map1: {
    [K: string]: number
  };
  map2: {
    [K: string]: string
  };
}

const d: Data = {
  map1: {},
  map2: {},
};

function dataSet<T extends keyof Data, V extends Data[T][string]>(d: Data, dataKey: T, key: string, val: V) {
  d[dataKey][key] = val;
}


// works:
dataSet(d, 'map1', 'alpha', 3);
dataSet(d, 'map2', 'beta', 'charlie');

// Errors
dataSet(d, 'map1', 'alpha', 'charlice');
dataSet(d, 'map2', 'beta', 3);

操场

您可以使用一些条件类型来推断地图的键和值类型。 调用set时会遇到类型问题,因为 TS 将d[dataKey]推断为Map<string, number> | Map<string, string> Map<string, number> | Map<string, string> 但是我们知道我们在这里做什么,所以我们可以安全地添加@ts-expect-error行。

我在操场上的 TS v4.1.5 上对此进行了测试。

interface Data {
  map1: Map<string, number>;
  map2: Map<string, string>;
}

const d: Data = {
  map1: new Map(),
  map2: new Map(),
};

type GetKey<M extends Map<any, any>> = M extends Map<infer K, any> ? K : never;
type GetValue<M extends Map<any, any>> = M extends Map<any, infer V> ? V : never;

function dataSet<K extends keyof Data>(d: Data, dataKey: K, key: GetKey<Data[K]>, val: GetValue<Data[K]>) {
  // @ts-expect-error
  d[dataKey].set(key, val);
}

// This now enforces the types correctly.
dataSet(d, 'map1', 'alpha', 3);
dataSet(d, 'map2', 'beta', 'charlie');
dataSet(d, 'map2', 'foo', true); // Errors as expected.

这里还有一个到操场链接

更新

在推理的帮助下是可以做到的。 您应该推断整个数据结构。

interface Dictionary {
  map1: Map<string, number>;
  map2: Map<string, string>;
}

const d: Dictionary = {
  map1: new Map(),
  map2: new Map(),
};

const dataSet = <
  MapKey,
  MapValue,
  HashMapKey extends string,
  HashMap extends Record<HashMapKey, Map<MapKey, MapValue>>,
  >(hashMap: HashMap, dataKey: HashMapKey, key: MapKey, val: MapValue) =>
  hashMap[dataKey].set(key, val);

// Ideally, called like:
dataSet(d, 'map1', 'alpha', 2) // ok
dataSet(d, 'map1', 'alpha', 'str') // expected error

dataSet(d, 'map2', 'beta', 'charlie');
dataSet(d, 'map2', 'beta', 4); // expected error

HashMapKey - 推断map1map2

HashMap - 推断整个数据结构(参数)

操场

如果你对函数参数推断感兴趣,可以查看我的文章

暂无
暂无

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

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