Choosing classes as types over interfaces & type aliases in TypeScript.

Thabo Ambrose
2 min readOct 29, 2022

Typed code is a breeze to work with

Why would I use a class as a type?

Because interfaces and type aliases are not present at runtime.

By the way, I’m coding to the interface now.

Yeah, things change, minds change. So stop right here.

Example usecases

Type guards

@Input() content!: Type<unknown> | TemplateRef<Element>

In place of Type<unknown>, you can also use ComponentType<unknown>
imported from the CDK (@angular/cdk/portal) or maybe that’s a bad idea. I dunno.

With the above declaration, I can do the following because classes and functions exists at runtime. Type and TemplateRef are both construct-able types.

Type<T> represent a constructable type T which can be a Component class or some other type representing a type of an object. We do not know T or what will be T at this point so we use the unknown type.

Because Component is an interface, we cannot use it in type guards. That is why we use the FunctionConstructor type Type to guard for a component input which will result to typeof === ‘function’.

But even this does not really give you confidence that users of this component will know that a property of a component expects a component of type X. But does help with preempting that a property accepts a constructed type or not. See example code snippet below

TemplateRef<T> represents an abstract class type therefore we can use the instanceof TemplateRef to guard for a template input.

if(typeof this.content === 'function') {
console.log('Handle component instantiation');
}

if(this.content instanceof TemplateRef) {
this.contentTemplate = this.content;
}

TypeScript will give an error if the type guard is used with an interface or a type alias. Some error like below

SomeType only refers to a type, but is being used as a value here

Initial class property values

Another benefit of using classes as types is that you can avoid the below TS error in classes when defining class properties.

Property 'someProperty' has no initializer and is not definitely assigned in the constructor.

With classes you can quiet the error by using the class type initial instance as the default value by instantiating it like below.

someProp: SomeClass = new SomeClass();

Using classes as types is not always viable, the scopes where I find using classes as types beneficial is with:

  • State stores with NGXS. When defining the shape of the state object and with action signatures.
  • When defining base classes for polymorphic UI components. Example, a card component that extends a base class with properties that are shared buy other kinds UI card components.

--

--