typescript 如何将字符串数组转换为类型级别的属性名称?

rsl1atfo  于 2022-11-18  发布在  TypeScript
关注(0)|答案(2)|浏览(268)

给定函数

function invert(arr: string[]) {
  let result = {};

  arr.forEach((x, i) => {
    result[x] = i;
  })

  return result; // insert type cast here
}

基本上,我在寻找的是一种VSCode中的intellisense方法,它确切地告诉我在结果对象中有哪些属性可用,因此给定const example = invert(['foo', 'bar', 'baz'])
自动完成应显示以下内容:

我目前的方法涉及到转换result as {[K in T[number]]: K},其中Ttypeof arr的别名,但是这种方法没有给予任何自动完成功能。
我写什么来让TypeScript显示这些东西?
接口{[name]: number}仅仅是一个简化的例子(出于保密协议的原因),如何将数组Map到属性名的主要问题仍然是所有问题的核心。

oyxsuwqo

oyxsuwqo1#

如果您愿意使用as const定义数组,这是一个可行的解决方案

type ArrayToNumberMapType<K extends string, A extends readonly K[]> = {[K in A[number]]: number}

function invert<K extends string, A extends readonly K[]>(arr: A): ArrayToNumberMapType<K, A> {
  let result: ArrayToNumberMapType<K, A> = {} as ArrayToNumberMapType<K, A>;

  arr
    .forEach(
      (x: K, i: number) => {
        result[x] = i;
      }
    )

  return result;
}

const someArray = ['foo', 'bar', 'baz'] as const
const mappedObject = invert(someArray)
mappedObject. // .foo, .bar or .baz
jk9hmnmh

jk9hmnmh2#

正如前面的注解所指出的,实现这一点的一种方法是使invert成为泛型。目前arr只是一个string[]。这就是为什么您的Map类型不起作用。

function invert<T extends string>(arr: T[]): Record<T, number> {
  let result = {} as Record<T, number>;

  arr.forEach((x, i) => {
    result[x] = i;
  })

  return result
}

这个简单的泛型实作会产生Record<T, number>,其中T包含数组内的索引键。

const example = invert(['foo', 'bar', 'baz'])
// const example: Record<"foo" | "bar" | "baz", number>

Playground
这是可行的,但我们仍然可以改进一下。目前,键只输入为number,即使我们确切地知道 * 哪个 * 数字。

function invert<T extends string[]>(arr: [...T]): { 
    [K in keyof T & `${bigint}` as T[K] & string]: K extends `${infer N extends number}` ? N : never 
} {
  let result = {} as any;

  arr.forEach((x, i) => {
    result[x] = i;
  })

  return result
}

在这个实现中,我们直接MapT的元素,过滤掉数字键,然后把索引K转换成数字。
这会产生下列传回型别:

const example = invert(['foo', 'bar', 'baz'])
// const example: {
//     foo: 0;
//     bar: 1;
//     baz: 2;
// }

Playground

相关问题