Typescript, oh I love it. Take stackoverflow survey, or ask any developer, most of them do. All major UI libraries/frameworks are (following Angular way) adding Typescript support now. Need to write a little extra boilerplate (use json to ts extension), but the benefits of type checking, intellisense and instant visual feedback outweigh the extra work.
I had this confusion where both interface and class gets the work done, but which one to use and when?
TLDR
Use interface, avoid class unless there is any special requirement that cannot be done with interface.
Classes add to js file size, after compiling
.ts
to.js
, while interfaces do not
Classes take extra lines
Lets take scenario, where we want to give structure to a pizza object. I can use interface or an object.
Pizza Interface
pizza-interface.ts
interface Pizza {
variant: string;
size: string,
price: number;
extraCheese: boolean;
takeAway: boolean;
}
const myPizza: Pizza = {
variant: 'Maxican green wave', size: 'medium', price: 550, extraCheese: true, takeAway: false,
}
console.log(myPizza);
pizza-interface.js
var myPizza = {
variant: 'Maxican green wave', size: 'medium', price: 550, extraCheese: true, takeAway: false
};
console.log(myPizza);
Pizza Class
pizza-class.ts
class Pizza {
variant: string;
size: string;
price: number;
extraCheese: boolean;
takeAway: boolean;
constructor(variant: string, size: string, price: number, extraCheese: boolean, takeAway: boolean) {
this.variant = variant;
this.size = size;
this.price = price;
this.extraCheese = extraCheese;
this.takeAway = takeAway;
}
}
const myPizza = new Pizza('Maxican green wave', 'medium', 550, true, false);
console.log(myPizza);
pizza-class.js
var Pizza = /** @class */ (function () {
function Pizza(variant, size, price, extraCheese, takeAway) {
this.variant = variant;
this.size = size;
this.price = price;
this.extraCheese = extraCheese;
this.takeAway = takeAway;
}
return Pizza;
}());
var myPizza = new Pizza('Maxican green wave', 'medium', 550, true, false);
console.log(myPizza);
More the lines in your
.js
, more is its size
Usecase for class
Lets take a scenario of employee salary, where HRA, PF contribution is dependent on the basic amount. So if I want to provide structure for salary object with least effort, I might go with class instead of interface here.
salary.ts
class SalaryComponents {
basic: number;
pf: number;
hra: number;
professionalTax: number;
constructor(basic: number, state: string) {
this.basic = basic;
this.hra = basic * 0.5;
this.pf = basic * 0.12;
this.professionalTax = this.getProfessionalTax(state);
}
getProfessionalTax(stateName: string): number {
return 2000; // dummy value
}
}
const emp1 = new SalaryComponents(1000, 'Tamil Nadu');
console.log(emp1);
/** Output
{
basic: 1000,
hra: 500,
pf: 120,
professionalTax: 2000
}
*/
With just 2 inputs, i could create an object. Pretty neat huh!!
This is the only scenario is could think of, where class is more effective. Hope it was helpful. I am open for any constructive criticism/feedback.
Top comments (13)
In this context
class
is a waste i.e. it isn't carrying its weight. On a personal level I'm not fond of classes as they tend to conflate type space and value space (just like TypeScript does with regular function declarations and expressions)On introduction I'd go with
By going with an object literal TypeScript can verify that
myPizza
conforms with thePizza
type and as a bonus the object properties act as "named parameters". For examplevariant
andsize
are bothstring
but there is no confusing them as you have to refer to them by name.Creating
Pizza
's all over the place I'd move to:Now I can mass produce
Pizza
objects - yet don't have to bother with eithernew
orthis
(not that they'll slow me down if it came to it).However there is a trade off by going with a factory function (or a for that matter a constructor) - TypeScript won't be able to detect whether you swapped
variant
withsize
orextraCheese
withtakeAway
.Bottom line: I don't go "class-oriented" until there is a clear benefit - until then I'll stay function-oriented.
As to the issue of not using parameter properties - one could simply write:
Is it convenient? Perhaps.
Is it correct? Not in my book.
A type is a specification that a value has to conform to, to be considered a member of that type. It's a "target" that should be spelled out in explicit detail separately and prominently - not be buried inside the code that is responsible for creating the value.
For all your examples, does intellisense work on each?
Are you familiar with Does Visual Studio Rot the Mind?:
So if IntelliSense can't keep up - that's its problem . And no, I don't use Visual Code (The IDE Divide).
I personally stick to type aliases.
interface
comes in handy when it is necessary to augment existing classes with an interface merge:Type
actually looks better than interface.. so for api response and request object structure, I'll be using type. And interface only to implement any class. Thanks for your time and explaining everything.You can use
public
keyword in your constructor to avoid boilerplate codeHave you considered dependency injection as a scenario? For example, what if I have a service class I depend on? If I inject that into my class constructor, I can easily test it.
Hi! I believe this scenario doesn't work anymore. If I'm not mistaken, since Angular 9 or 10, you can't use Angular features (like DI, Inputs/Outputs and Lifecycle hooks) in non-annotated classes.
So, for models, I believe interfaces are the best way to go, unless you have custom behaviour/properties (kinda like transient properties in Java entities, which are calculated from base properties).
I apologize, I am not familiar with Angular and meant my comment as a general note for ES6/TypeScript. I appreciate your insight on Angular mechanics!
Heey, no need to apologize! It's all about sharing knowledge! Cheers!
There is no need to add the properties on a ts class if they match the constructor. Simply add an access modifier (public for example):
"constructor(public xyz:string) {}"
And it will be automatically added as a property of the class and assigned the value.
Actually I did not define any target in tsconfig and by default it compiled to ES5 probably.
Let me check this one again. Thanks for pointing out.
Good point, and my thoughts too.