在TypeScript中迭代接口属性

92

我需要将接口属性映射到对象:

interface Activity {
  id: string,
  title: string,
  body: string,
  json: Object
}

我目前正在做:

headers: Array<Object> = [
  { text: 'id', value: 'id' },
  { text: 'title', value: 'title' },
  { text: 'body', value: 'body' },
  { text: 'json', value: 'json' }
]

这变得非常重复。我希望的是像这样的东西:

headers: Array<Object> = Activity.keys.map(key => {
  return { text: key, value: key }
})
6个回答

51

你不能,因为 JavaScript 不支持接口,所以接口只能在编译时使用。

你可以做的是类似这样:

const Activity = {
    id: "",
    title: "",
    body: "",
    json: {}
}

type Activity = typeof Activity;
const headers: Array<Object> = Object.keys(Activity).map(key => {
    return { text: key, value: key }
});

(在 Playground 中查看代码)


4
也许这是一个显而易见的问题 - 第二个Activity声明(作为类型)如何不遮盖第一个声明(作为常量)? - Freewalker
10
@LukeWilliams 第一个(const)定义是一个值,而第二个定义则是类型定义。例如,如果你没有第二个定义,并且你这样写:let a: Activity; 你将会得到以下的错误:"Cannot find name 'Activity'"。编译器将在不同的时间/情境使用这些定义。 - Nitzan Tomer
1
如果我们希望接口的属性是可选的,我们能否实现相同的效果? - velociraptor11
@velociraptor11,无法将编译时类型转换为运行时类型,因此不行。您可以做的是从结果数组中过滤掉所有具有“undefined”(或“null”)值的项。 - Nitzan Tomer
2
请删除“type Activity = typeof Activity;”这一行。Object.keys只能用于对象,不能用于类型。 - Sebastian Thees
叹息。这是我希望TS编译器更主动一些,在编译时将Activity.keys替换为['id', 'title', 'body', 'json']的众多场合之一。 - O. R. Mapper

33
如果您可以在编译时添加它,并且使用的是TypeScript >= 2.4.1,您可以尝试这里提出的方法。基本上,您应该添加ts-transformer-keys依赖项自定义转换器,像基本转换器一样,然后您就可以像这样列出属性:
import { keys } from 'ts-transformer-keys';

interface Props {
    id: string;
    name: string;
    age: number;
}
const keysOfProps = keys<Props>();

console.log(keysOfProps); // ['id', 'name', 'age']

3
我该如何在一个纯 TypeScript 项目中使用它,而不需要 webpack? - TacB0sS
4
好的建议!我能够在一个 Gulp 脚本中很容易地使用它进行编译。如果你需要参考我的片段,这里是链接:https://github.com/Sitetheory/stratus/blob/fa288b81d3860c40795b89ca9c1c4b17cdcf9f41/gulpfile.js#L28-L38 - Alex Gurrola
1
如果您没有使用打包工具,最简单的方法是使用 ttypescript。只需安装它并将插件添加到您的 tsconfig.json 文件中即可。 - T. Dayya
使用 create-react-app 太难了,很难上手 :( - Daniel
@Daniel react-app-rewired 在 CRA 中很好地支持添加扩展功能。 - Unique Divine

7
如果您想保留接口功能,可以按照@Nitzan Tomer所说的进行操作。接口是类型系统的一部分,因此它们只在编译时才有意义,因为它们在转换后的代码中被省略。
class Activity {
    public id: string = '';
    public title: string = '';
    public body: string = '' ;
    public json: Object = {};
}

let activity = new Activity()

const headers: Array<Object> = Object.keys(activity).map(key => {
    return { text: key, value: key }
});

console.log(JSON.stringify(headers))

2
这种方法可能有点过度,但我使用它是因为我需要JSON模式来验证后端响应结构。从接口中获取键只是将TypeScript接口转换为JSON模式的一个不错的副作用:
通过使用TypeScript转换为JSON模式转换器,可以获取接口键以及它们的类型。生成的JSON对象很大且冗长,因此递归地构造简单的JS对象的解析辅助函数可能会派上用场。好消息是,JSON模式始终具有相同的结构,因此易于导航。

0

还有另一个npm包ts-interface-keys-transformer,它允许映射嵌套属性。

例如:

import { keys } from 'ts-interface-keys-transformer';

const configKeys = keys<Config>() as Array<{ name: string, type: string }>;
  for (const key of configKeys) {
    if (typeof key.type === 'string') {
      const value = key.name.split('.').reduce((p, current)=> p && p[current] || null, objectToCheck);

      if (!value) {
        throw new Error(`The '${key.name}' property must be provided.`);
      }
    }
  }

0
例如,如果我们有以下接口:
type Features = {
  darkMode: () => void;
  newUserProfile: () => void;
  isActive?: boolean
};

& 你想要映射的类型是一个使用 PropertyKeys 联合作为泛型类型的接口,如下所示:

type FeatureFlags = {
    darkMode?: boolean | undefined;
    newUserProfile?: boolean | undefined;
    isActive?: boolean | undefined;
}

那么我们可以像下面这样做。

type Features = {
    darkMode: () => void;
    newUserProfile: () => void;
    isActive?: boolean
};
type FeatureFlags = { [Property in keyof Features]?: boolean };

使用映射类型

& 如果您想要任何接口键的类型,例如darkMode | newUserProfile | isActive。那么您可以像这样做:type FeatureFlags = keyof Features索引访问类型


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接