网站搜索

TypeScript 泛型函数重载和/或类型保护


我正在尝试创建一个通用函数,以这种方式提取枚举和对象的键和值:

type Key = string | number | symbol;
type Dictionary<K extends Key, T> = Partial<Record<K, T>>;
type Obj = Dictionary<Key, unknown>; // stricter than object
type Enum = Dictionary<number, string>; // stricter than enum

function keys<K extends Key, T>(record: Record<K, T>): Array<K> {
  return Object.keys(record) as Array<K>;
}

function values<K extends Key, T>(record: Record<K, T>): Array<T> {
  return keys(record).map((key) => record[key]);
}

问题是,无论 anything 的键是什么,Object.keys(anything) 都返回 string[],更糟糕的是,当 anything 是一个枚举时,它还会将数值作为字符串返回(在我看来这显然是错误的),所以我为这种情况写了一个特定的函数:

function keys(record: Enum): Array<number> {
  return Object.keys(record)
    .map((x) => parseInt(x))
    .filter((x) => !isNaN(x));
}

现在我该如何将这段代码合并到通用键函数中呢?我猜可以在 K 上加一个类型保护,以防它是数字,但我尝试了很多方法都没有成功……

顺便问一下,有没有已经实现这个功能的“纯 TypeScript”库?

答案#1

这并不直观——但实际上假设 keyof T 来自 Object.keys 是不正确的——完整解释请参见 https://stackoverflow.com/a/55012175/2310450 ——但简而言之,需要记住 TypeScript 不会执行对象的额外属性检查,并且可能存在运行时原型污染的问题。

如果我理解你的问题正确的话——我相信你可以在你的过滤语句中使用 Extract 来提取枚举中的数字键。

function keys<E extends Enum>(record: E): Extract<keyof E, number>[] {
  return Object.keys(record)
    .map((x) => parseInt(x))
    .filter((x): x is Extract<keyof E, number> => !isNaN(x));
}

const res = keys(foo); // res: (0 | 1 | 124124)[]