0

I have a Typescript interface:

export interface BookModel {
  id: number;
  title: string;
  publishedAt: Date;
}

And I am using as:

let book: BookModel = { id: 1, title: "Title", publishedAt: new Date(2018, 10, 12); }

let valid = this.validate(Requirement.IsNew, book);

Where validate method is:

validate(requirement: Requirement, resource: any) : boolean {
  if (resource instanceof BookModel) {

  }
  if (resource instanceof PostModel) {

  }
  return false;
}

I get an error when trying to check if resource type is BookModel or PostModel or ...:

'BookModel' only refers to a type, but is being used as a value here.

I need to check the type as the validation process depends on type.

Miguel Moura
  • 36,732
  • 85
  • 259
  • 481
  • Does this answer your question? [Interface type check with Typescript](https://stackoverflow.com/questions/14425568/interface-type-check-with-typescript) – Majesty Nov 19 '19 at 12:03
  • As I see, your code supposed to make a runtime validation, while TS interface does not exist on that stage, therefore TS gives you an error. – Majesty Nov 19 '19 at 12:05

3 Answers3

0

I've been through this before, the way I solved it was by using an Abstract Class instead of type/interface.

  • All my models are interfaces. Isn't this Angular's recommendation? So I was trying to avoid changing everything to classes. – Miguel Moura Nov 19 '19 at 12:06
0

TypeScript type annotation don't exists in the runtime, also instanceof operator works only with prototypes, it means that you need to have custom prototype in the object to work with this operator (use function constructor or ES6 Class).

BookModel is plain object, there is no additional prototype to check (outside object prototype). That is why you cannot use instanceof, and as said before BookModel as a type cannot be used as a runtime value.

What we can use instead is type representation - the data structure. To differentiate BookModel from PostModel we need to find what props are different, find the discriminant. If you don't have any special property which differs between BookModel and PostModel, such can be created. Below I have used _type:

export interface BookModel {
  _type: 'Book', // special meta information as discriminant
  id: number;
  title: string;
  publishedAt: Date;
}

export interface PostModel {
  _type: 'Post',// special meta information as discriminant
  id: number;
  title: string;
  publishedAt: Date;
}

// below I removed first argument for readability
// check also that resource is now specified as a union type
function validate(resource: BookModel | PostModel) : boolean {
  if (resource._type === 'Book') {
    // here Book
  }
  if (resource._type === 'Post') {
    // here Post
  }
  return false;
}

let book: BookModel = { id: 1, title: "Title", publishedAt: new Date(2018, 10, 12), _type: 'Book' }

let valid = validate(book);

Thanks to _type we can apply custom logic by differentiate between structure representing specific types.

Maciej Sikora
  • 19,374
  • 4
  • 49
  • 50
  • I don't think it is a good solution. First of all, it is hard to avoid collisions, how will you guarantee, that all `_type` properties are unique? TS has no control over it, now it is your responsibility to keep track of that. And it looks messy, you are populating metadata with a trash, literally. Better use abstract classes than that... – Majesty Nov 19 '19 at 13:51
  • TS is not OOP only language, introduction of classes and abstract classes is bigger overhead then as you said this additional "trash". What I propose here is nothing strange, it is discriminated union - https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions. Its common practice. – Maciej Sikora Nov 19 '19 at 14:14
0

The right side of the instanceof needs to be a constructor function, and TypeScript will narrow down to:

  1. the type of the function’s prototype property if its type is not any

  2. the union of types returned by that type’s construct signatures

in that order

since interface is not kind of constructor, you got the type-check error.you may solve the problem by class implements interface.

Community
  • 1
  • 1
upon.gao
  • 66
  • 4