“编译时”方式获取定义接口的所有属性名称

问题描述 投票:0回答:4

我想创建一个通用的 TypeScript 类,用于渲染(作为 HTML 列表)实现特定

interface
的对象数组。

例如

class GenericListRenderer<T> {
  items: T[];

 constructor(listItems: T[], className?: string){
      this.items = listItems;
      ...
    }

    private getPropertyNames(): string[]{

    // What is the best way to access all property names defined in
    // TypeScript interface 'T' that was used in this generic?
    ...   
    }

    render(){
      var propNames: string[] = this.getPropertyNames();
      // render list with each item containing set of all 
      // key(prop name)/value pairs defined by interface 'T'  
      ...

    }
}

问:获取指定 () TypeScript 接口中定义的所有属性名称的“编译时”列表的最佳方法是什么?

与 C++ 模板一样,我相信 TypeScript 可以在“编译时”解析这些泛型,此时 TypeScript 类型信息(例如提供给用于实例化特定对象的泛型的接口)随时可用。

由于可能会提供所有必需的类型信息,我只是好奇是否有 TypeScript 扩展/工具可用于访问此信息,而无需对“普通”Javascript 对象进行过多的运行时过滤——这可能会因继承不明确而出现问题问题(例如,如果使用运行时、通用、Javascript (obj.hasOwnProperty(prop)) 来过滤属性,则所需的 TypeScript 继承接口属性可能会被过滤掉)。

这种有用的属性自省潜力可以在“编译时”使用 TypeScript 的类型元数据超集明确解决,而不是在所有此类类型信息都被丢弃时尝试在翻译后的 Javascript 中解析此信息。

如果存在标准(TypeScript)方法,我讨厌用可能不完美的 Javascript hack 来“重新发明轮子”。

typescript generics interface
4个回答
39
投票

这可以通过使用 https://github.com/Microsoft/TypeScript/pull/13940 引入的自定义转换器来实现,该转换器在 typescript >= 2.4.1 中可用。

我的 npm 包,

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']

11
投票

不可能在运行时检索该信息,并且默认情况下它们永远不会成为可能,除非您在编译期间存储它们。因此,解决方案是使用 ReflectDecorators 进行反射。

Here 是一篇优秀的文章,涵盖了在运行时检索编译时元数据的问题。简而言之:您向想要保留描述的界面添加一个装饰器,该装饰器将被转换为 JSON 对象,该对象将存储到代码本身中。在运行时,您将能够检索包含所有接口数据的 JSON 对象。现在这是实验性的(2016 年 2 月 11 日),但效果很好。

注意:默认情况下永远不会的原因基本上是 TS 的设计选择,不要使用元数据重载 js 代码(与 Dart 不同)。


1
投票

在运行时,所有类型信息都会被删除,因此您能做的最好的事情就是枚举其中一个对象的属性。这将为您返回所有属性,甚至是那些不在指定接口上的属性。

class GenericListRenderer<T> {

    constructor(private items: T[], private className?: string){

    }

    private getPropertyNames(): string[] {
        var properties: string[] = [];

        if (this.items.length > 0) {
            for (var propertyName in this.items[0]) {
                console.log(propertyName);
                properties.push(propertyName);
            }
        }

        return properties;
    }

    render(){
      var propNames: string[] = this.getPropertyNames();
    }

}

class Example {
    constructor(public name: string) {

    }
}

var example = new Example('Steve');

var a = new GenericListRenderer<Example>([example]);

a.render();

还有

Object.keys()
,它可以返回所有属性,尽管它仅在 IE9 及更高版本中受支持。

如果您可以为您想要使用属性执行的操作提供更多用例,则可能会使用属性或其他机制为您提供替代解决方案。


0
投票

您可以尝试 Accelatrix,因为它包含一个在运行时工作的 JavaScript 类型自省系统:

https://www.nuget.org/packages/Accelatrix

https://www.npmjs.com/package/accelatrix

https://github.com/accelatrix/accelatrix

A型自省系统

JavaScript 的类型系统得到增强,包括四个基本操作:

- GetHashCode()
- GetType()
- Equals()
- ToString()

您现在可以像在 C# 中一样在运行时处理 JavaScript 中的类,例如:

var myDog = new Bio.Mammal(8);  
var myCat = new Bio.Feline(8, 9);

var timeIsSame = (new Date()).Equals(new Date());                //true           
var areEqual = myDog.Equals(myCat);                              // false           
var myCatType = myCat.GetType();                                 // Bio.Feline           
var myCatBaseType = myCat.GetType().BaseType;                    // Bio.Mammal           
var isAnimal = myCat.GetType().IsAssignableFrom(Bio.Animal);     // true           
var enums = Bio.TypesOfLocomotion.GetType();                     // Accelatrix.EnumType 

// 示例类:

export namespace Bio
{           
    export enum TypesOfLocomotion           
    {           
        Crawl,           
        Swim,           
        Walk,           
        Fly,           
    }           

    abstract class LivingBeing
    {
        public isExtinct = false;
    }

    export abstract class Eukaryotes extends LivingBeing
    {
        private locomotion: TypesOfLocomotion = null;

        public get Locomotion(): TypesOfLocomotion
        {
            return this.locomotion;
        }
        public set Locomotion(value: TypesOfLocomotion)
        {
            this.locomotion = value;
        }
    }

    export class Animal extends Eukaryotes
    {
        public isAnimal = true;

        public constructor()
        {
            super();
        }
    }
    
    export class Mammal extends Animal
    {
        private readonly numberOfTits: number;

        public constructor(numberOfTits: number)
        {
            super();
            this.numberOfTits = numberOfTits;
        }

        public get NumberOfTits(): number
        {
            return this.numberOfTits;
        }

        public SayHello(): string
        {
            return "Hello";
        }
    }

    export class Feline extends Mammal
    {
        private readonly numberOfLives: number;

        public constructor(numberOfTits: number, numberOfLives: number)
        {
            super(numberOfTits);
            this.numberOfLives = numberOfLives == null ? 9 : numberOfLives;
            this.Locomotion = TypesOfLocomotion.Walk;
        }

        public get NumberOfLives(): number
        {
            return this.numberOfLives;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.