【问题标题】:Offsetting an array of dictionaries by a string literal通过字符串文字偏移字典数组
【发布时间】:2021-09-04 17:55:04
【问题描述】:

我有三个相同类型的变量:

const foo = { name: "foo", age: 12, color: "red" } as const;
const bar = { name: "bar", age: 46, color: "blue" } as const;
const baz = { name: "baz", age: 52, color: "green" } as const;

然后我将它们填充到一个数组中,如下所示:

const arr = [foo,bar,baz];

我想做的是将这个数组(基于name 作为唯一标识符)转换为以“名称”为键的字典。至关重要的是,我需要将键保留为字符串文字类型,以便我可以像这样使用这些属性:

const dict = arrayToDict(arr);
// `dict.foo` is a known key of `dict`
console.log(dict.foo.age);

现在让我从好消息开始,使用我的简单示例可以做到这一点。以下功能将起作用:

export function arrayToObjectName<T extends { name: S }, S extends PropertyKey>(
  arr: readonly T[]
) {
  return arr
    .reduce((acc, v) => ({ ...acc, [v.name]: v }), {} as { [V in T as V["name"]]: V });
}

但是,此函数仅在“名称”已被硬编码后才有效。相反,我希望能够构建一个通用函数,该函数将您要使用的属性作为函数的一部分。我尝试的是这样的:

function arrayToObject<S extends PropertyKey>(prop: S) {
  return <T extends { [prop]: S }>(
    arr: readonly T[]
  ) => {
    return arr
      .reduce((acc, v) => ({ ...acc, [prop]: v }), {} as { [V in T as V[S]]: V });
  };

不幸的是,这在两个地方失败了。首先第二个函数签名是不允许的(见错误):

类型文字中的计算属性名称必须引用类型为文字类型或“唯一符号”类型的表达式。

最后一行中的V[S] 分配也失败并出现错误:

类型 'V[S]' 不能分配给类型 'string |号码 |符号'

现在很奇怪,即使如果我使用严格类型(我总是这样做),这些类型错误会阻止我进行转换,但从这个函数中推断出的类型确实有效!

有谁知道我如何实现创建保留类型的通用函数的雄心?在下面的操场中,您可以看到这两个函数以及展示结果的类型测试。


我现在想出了一个替代的通用实现,这也“有效”但仍然有一个错误:

export type Narrowable =
  string | number | boolean | symbol | object | undefined | void | null | {};

function arrayToObject<
  S extends PropertyKey>(prop: S) {
  return <N extends Narrowable, T extends Record<keyof T, N> & Record<S, T[S]>>(
    arr: readonly T[]
  ) => {
    return arr.reduce(
      (acc, v) => ({ ...acc, [prop]: v }), {} as { [V in T as V[S]]: V }
    );
  };
}

关于 V[S] 的错误与之前的错误相同,但上述类型确保 S 必须是 keyof T 并消除了我遇到的第一个错误。

【问题讨论】:

    标签: typescript typescript-typings typescript-generics


    【解决方案1】:

    好的,我的“替代解决方案”实际上非常接近,但它需要调整并具有以下实现:

    export type Narrowable =
      string | number | boolean | symbol | object | undefined | void | null | {};
    
    export function arrayToObject<S extends PropertyKey>(prop: S) {
      return <N extends Narrowable, T extends Record<keyof T, N> & Record<S, any>>(
        arr: readonly T[]
      ) => {
        return arr.reduce(
          (acc, v) => ({ ...acc, [v[prop]]: v }), {} as { [V in T as V[S]]: V }
        );
      };
    }
    

    现在可以按预期工作了;所以下面的代码:

    const foo = { name: "foo", age: 12, color: "red" } as const;
    const bar = { name: "bar", age: 46, color: "blue" } as const;
    const baz = { name: "baz", age: 52, color: "green" } as const;
    
    const dict = arrayToDictionary("name")([foo,bar,baz]);
    

    将导致 dict 成为强类型字典,其中包含键 foobarbaz,并且每个键都具有每个键的完整 type-literal 定义对象。

    如果有人有更好或更简洁的答案,我很乐意接受他们的而不是我的。

    【讨论】:

      猜你喜欢
      • 2017-03-27
      • 1970-01-01
      • 2022-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多