Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine Typescript property type with reflection at runtime

When transpiling TypeScript to JavaScript, my understanding is that TypeScript type information is lost and features like reflection will only work in a very limited manner. We're using JavaScript reflection at runtime, which understandably has limited "knowledge" about TypeScript types.

Could there be a way to retrieve TypeScript type information at runtime?

Let's take the following snippet, available on codepen.io:

class Book {
    public title: string;
    public isbn: string;
}

class Author {
    public name: string = '';
    public books: Book[] = [];

    constructor() {
        const r = Reflect.getOwnPropertyDescriptor(this, 'books');

        console.log(`typeof: ${typeof this.books}`);
        console.log(`constructor: ${this.books.constructor.name}`);
        console.log(r);
    }
}

const author = new Author();

The logs output "object", "Array" and:

Object {
    configurable: true,
    enumerable: true,
    value: [],
    writeable: true
}

I want to iterate over the properties of Author, and determine the type of Author.books or any property. I am expecting to be able to establish at runtime that, Author.books is an Array of Books. Simply knowing that it is an object or an array does not help with what I am trying to achieve at all.

Any ideas how this could be achieved?

like image 678
josef.van.niekerk Avatar asked May 08 '17 08:05

josef.van.niekerk


1 Answers

I'm going to hazard an answer and say it cannot be done with what is available to you by default - you'd need to parse the d.ts to get true reflection of all the types.

As you say; the array is an instance of Array, the only way to infer what type the array is supposed to contain is to pass the class of the desired type. So, you could do something like this:

class Book {
  public title: string;
  public isbn: string;
}

type Newable<T> = {
  new (): T;
}

class Author<T> {
  public name: string = '';
  public books: T[] = [];

  constructor(bookRef: Newable<T>) {
    console.log(`name: ${bookRef.name}`);
  }
}

const author = new Author(Book);

Not amazing, and it only works if Book is a class.

typescript can also infer the type you use for Book from what you pass to the constructor:

enter image description here

bookRef.name will get you the class name at runtime.

like image 166
Meirion Hughes Avatar answered Sep 29 '22 10:09

Meirion Hughes