16

TypeScript: I have a method in the DataProvider class with a method getTableData:

public static getTableData<T extends DataObject>(type: { new(): T}): Array<T> { ... }

this works perfectly when I code:

let speakers = DataProvider.getTableData(Speaker);  // where Speaker is a class

now I want to call this from a generic Class:

export class ViewModelBase<T extends DataObject> {   
  public getData(): Array<T> {
    return <T[]> DataProvider.getTableData(T);
  }
}

Now I get a Cannot find name 'T' error for the T parameter I pass to getTableData. How should getTableData be called?

update: With the help of @Paleo I came up this:

export class ViewModelBase<T extends DataObject> {   

  constructor(private dataObjectClass: { new(): T}){}

  public getTableData(): Array<T> {
    return <T[]> DataProvider.getTableData<T>(this.dataObjectClass);
  }
}

the thing is that although I have already told in: class SpeakerViewModel extends ViewModelBase<Speaker> { ... } that I want it to be a ViewModel for Speaker I still have the instantiate the SpeakerViewModel like:

let vm = new SpeakerViewModel(Speaker);

although I have already told it is all about Speaker. I guess I still don't fully understand this.

3
  • In DataProvider.getTableData(T), the parameter T is defined nowhere. Commented May 27, 2016 at 13:14
  • "that I want it to be a ViewModel for Speaker I still have the instantiate the SpeakerViewModel like:let vm = new SpeakerViewModel(Speaker);" sorry, I am confused. Can you rephrase that? What is the code you want to omit? Commented Apr 24, 2017 at 23:53
  • I'm not sure what changed, but I was only able to get this to work with the parameter signature like { new(...args: any[]): T }. I have a suspicion that it's because my class constructor had parameters. Commented Dec 1, 2019 at 0:20

3 Answers 3

6

Generics are just metadata. They cannot be used as parameters when calling a function. Maybe you need something like this:

export class ViewModelBase<T extends DataObject> {
  constructor(private Cl: {new(): T}) {
  }
  public getData(): Array<T> {
    return DataProvider.getTableData<T>(this.Cl);
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

almost but your comment made me figure what I actually needed: constructor(private Cl: { new(): T}).... Thanks!
@BroncoOostermeyer OK. I edited to fix the code for future readers.
3

maybe this would help:

export abstract class BaseEntity {
  public static from<T extends BaseEntity>(c: new() => T, data: any): T {
    return Object.assign(new c(), data)
  }
  public static first<T extends BaseEntity>(c: new() => T, data) {
    if (data.rows.length > 0) {
      let item = data.rows.item(0);
      return BaseEntity.from(c, item);
    }
    return null;
  }

}

This class can be extended by others so you could call methods on the base class or on its subclasses.

For instance:

return Product.first(Product, data);

Or:

return BaseEntity.first(Product, data);

See how from() method is called from inside first()

Comments

-2

How about defining a base type and extending it? Then your function could expect the base type, and you could call it with the extended type. e.g:

export interface BaseData {
  key: object
}

Then:

import { BaseData } from 'baseDataFile'

export interface DerivedData extends BaseData {
  key: someObjectType
}

Now:

import { BaseData } from 'baseDataFile'

export const someFunc = (props: BaseData) => {
    // do some stuff
    return something 
}

Finally:

import { DerivedData } from 'derivedDataFile'

const myData: DerivedData = something as DerivedData
const myNewData = someFunc(myData)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.