如何从打字稿中的数组值创建对象键

APu*_*APu 2 javascript types typescript

如何在打字稿中输入以下函数,以便获得自动完成和错误预防功能。

使用打字稿 4.7.4

// reference fn
function doSomething(list) {

    const data = {};

    list.map(item => {
        data[item] = 'value type string|number|bool|null'   
    });

    return data;
}

// calling it like
const data = doSomething([
    'phone_number',
    'customer_email'
]);

// get IDE autocomplete here (for only properties inside data)
console.log(data.phone_number);
console.log(data.customer_email);

// typescript yell when try to access invalid properties
console.log(data.phone_numbersss);
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 5

只要所使用的数组在函数调用时实际上是一个编译时常量(因此 TypeScript 知道它的值是什么),您就可以做到这一点。否则,TypeScript 不知道数组内容是什么,并且必须采用任何字符串。至少有两种方式可以满足约束:

\n
    \n
  1. 您可以通过对其应用常量断言来直接说数组是常量:

    \n
    const names = ["phone_number", "customer_email"] as const;\n// \xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92^^^^^^^^\n
    Run Code Online (Sandbox Code Playgroud)\n
  2. \n
  3. 您可以在调用时使用数组文字,并使用可变元组类型作为参数的类型(更多内容见下文)。

    \n
  4. \n
\n

一旦我们知道数组的内容是 TypeScript 的编译时常量,我们就可以使用映射类型来映射一个数组,该数组扩展string[]为一个对象,并以数组元素作为键和所需的属性类型:

\n
type DoSomethingResult<T extends readonly string[]> = {\n    [Key in T[number]]: string | number | boolean | null;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

实现该函数需要创建对象(而不是数组)并为属性赋予一些值;我选择了null

\n
function doSomething<T extends readonly string[]>(list: [...T]): DoSomethingResult<T> {\n    // Variadic tuple type \xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92^^^^^^\n    return Object.fromEntries(list.map((key) => [key, null])) as DoSomethingResult<T>;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是电话:

\n
const data = doSomething([\n    "phone_number",\n    "customer_email"\n]);\n
Run Code Online (Sandbox Code Playgroud)\n

然后所有给定的测试用例都可以工作。

\n

游乐场链接

\n

请注意,数组必须是数组文字;这行不通:

\n
// WON\'T WORK AS DESIRED\nconst names = [\n    "phone_number",\n    "customer_email"\n];\nconst data = doSomething(names);\n
Run Code Online (Sandbox Code Playgroud)\n

TypeScript 将数组的类型推断为string[],而不是["phone_number", "customer_email"]as const(如果你在数组上添加一个,它就会起作用。)

\n
\n

如果您必须支持没有可变参数元组类型(在 v4.0 中引入)的 TypeScript 版本,您可以使用as const以下版本:

\n
function doSomething<T extends readonly string[]>(list: T): DoSomethingResult<T> {\n    // Using `T` directly as the type \xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92\xe2\x88\x92^\n    return Object.fromEntries(list.map((key) => [key, null])) as DoSomethingResult<T>;\n}\n// ...\nconst data = doSomething([\n    "phone_number",\n    "customer_email"\n] as const);\n//^^^^^^^^\n
Run Code Online (Sandbox Code Playgroud)\n

Playground链接使用v3.9.7

\n

  • @APu 只需使用 `(list: [...T])` 而不是 `(list: T)`。请参阅[可变元组类型文档](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#variadic-tuple-types) (2认同)