I am new to Typescript and ran into the following question when trying to implement a interface in a class:
interface TestInterface {
x: number
}
class Test implements TestInterface {
constructor() {
this.x = 0
}
}
// Class 'Test' incorrectly implements interface 'TestInterface'.
// Property 'x' is missing in type 'Test' but required in type 'TestInterface'.
But if I add x: number to Test before constructor, it works. So I am guessing if you wanted to check the type of a property initialized in the constructor, you have to specify that on the class itself. Then what is implement and the interface for? And if I want to use the interface to check the type of its instances, wouldn't it be repetitive since I have to write it in two places?
Code in a constructor is not something that Typescript can check to determine if your class correctly implements the interface. It would be impossible for Typescript to check all the assignments in your code to ensure the types are correct, so you need to declare the types statically. The point of interface and implements is not to reduce the amount of code you have to write, it's to declare the type of objects in your code, so that Typescript can statically check that your code is self-consistent with regard to types.
By saying the class implements the interface, you are effectively asking Typescript to keep you honest and check that the types are indeed consistent. If Typescript merely trusted that you'd do the right thing in your constructor, it would not be very effective! Note that constructors may contain conditional logic, loops and other code that mean it is not possible even in principle for Typescript to analyse the constructor code to determine if you are keeping the contract you commit to with implements.
So I am guessing if you wanted to check the type of a property initialized in the constructor, you have to specify that on the class itself.
You are exactly right!
Then what is implement and the interface for?
An interface is intended to be a "contract". Here's a simple example:
interface Connectable {
connect: () => Promise<void>
}
const robustConnect = async (connection: Connectable) => {
try {
await connection.connect();
} catch (error) {
console.warn(`there was an error, let's try once more`);
await connection.connect();
}
}
In this example, we've typed the function robustConnect so that it can receive just any object that has a connect method. This is the most useful usage of an interface: giving a name to a type so that it is more legible.
In other OO languages, an interface would be the only way to declare a type like that, and you would have to extend the interface to use robustConnect. Otherwise, the compilation step would fail. Since javascript isn't inherently an object-oriented language and is dynamically typed, that restriction isn't there. So the implements keyword can feel useless. At best, you could use it to help yourself when writing classes: just use the implements keyword and let the TypeScript compiler tell you what you are missing. But that's about it.
Hope this helps.
Low key You need to learn Object Oriented Programming. Your question is a core principle of oop.
When you find a new job, you sign a contract, the contract specifies your role, duties, behavior and more; so you need to follow the contract so if you implement an interface your class need to follow the structure of your interface.
If for instance you find that redundant instead of creating a class use pure objects
const obj:Obj ={
name: “eneto”
};
Plus also your statement “But if I add x: number to Test before constructor, it works. So I am guessing if you wanted to check the type of a property initialized in the constructor, you have to specify that on the class itself. Then what is implement and the interface for?” doesn’t have sense, because typescript checks your code at compilation fase, but once your code compiles it becomes JavaScript which doesn’t have static types
Related
I have searched everywhere but didn't get a clue on how I can achieve such a behavior. I'm new to typescript, thus forgive me if it's a dumb question.
The code below is a very simplified version of what I'm looking for:
const library = { // could also be a class if necessary
registerMethod(name: string, method: () => any): void {
this[name] = method;
}
}
library.registerMethod('couldBeWhatever', () => { }); // could be called any number of times with custom functions and names
I'm looking for a way (not necessarily the one above) to register methods of a class or object at startup with allowing completion on those dynamic functions anyway. Is that possible in typescript ?
I'm guessing that by "at runtime" you mean that you want the compiler to perform control flow anlysis to try to predict what methods will be available by examining TypeScript code that calls libray.registerMethod(). Obviously there is no way to get completion in your IDE for anything that will only be known about at runtime, such as if your code generated a random method name string with Math.random() or pulled the method name from user input or API response or something.
If so, then you might want to use assertion functions. An assertion function narrows the type of one of its arguments (you can also have an assertion method that narrows the type of the object on which it is called).
By narrowing, it means that the apparent type after the assertion function must be a subtype of the apparent type before the assertion function. For example, you can't make an assertion function turn a string into a number, but you can make it turn a string | number into a number. So one of the caveats of using control flow analysis to model mutation of library is that it cannot be used to make it incompatible with the original type. Your registerMethod() method looks like it will add new members to library, and adding members to an object type is a form of narrowing. But if you needed an alterMethod() that say, changes the type of the registered method from () => string to () => number, you wouldn't be able to do this.
Here's an example implementation of library with registerMethod() as an assertion method:
interface _Lib<T extends Record<keyof T, () => any>> {
registerMethod<K extends string, M extends () => any>(
name: K,
method: K extends keyof T ? T[K] : M
): asserts this is Library<{
[P in K | keyof T]: P extends K ? M : P extends keyof T ? T[P] : never
}>
}
type Library<T extends Record<keyof T, () => any>> = T & _Lib<T>;
const library: Library<{}> = {
registerMethod(this: Library<any>, name: string, method: () => any): void {
this[name] = method;
}
}
There's a lot going on in there, but the basic idea is that library starts off as a Library<{}>, with only a registerMethod() method. When you call registerMethod() on a Library<T> with a name of type K and a method of type M, the compiler will narrow that Library<T> to something like a Library<T & Record<K, M>>. Meaning that, in addition to having registerMethod() and whatever was in T, it now also has a member at key K whose type is M.
You can test that it works:
library.registerMethod('couldBeWhatever', () => "hello");
console.log(library.couldBeWhatever().toUpperCase()); // HELLO
library.somethingElse; // error, somethingElse does not exist
library.registerMethod('somethingElse', () => 123);
console.log(library.couldBeWhatever().toUpperCase()); // still HELLO
console.log(library.somethingElse().toFixed(2)); // "123.00"
Hooray, it works! There are caveats that go along with assertion functions, though, which may or may not matter for your use case.
First of all, in order to use an assertion function you need it or the object you call it on to be manually annotated and not inferred. That's why I had to literally annotate library above as being Library<{}>. This is currently a design limitation of TypeScript. See microsoft/TypeScript#36931 among others, for more information.
Next, as with all control flow analysis narrowing in TypeScript, the compiler isn't all-knowing. It does not perform control flow analysis by simulating all possible ways to run the program and seeing which possible type narrowings stay true in all scopes. It would be prohibitively expensive to do so. Right now, what the compiler does is: when you cross a function boundary, the compiler resets any control flow narrowings. This is a reasonable trade-off, since the compiler cannot generally figure out when a function body will be called with respect to code outside the function body, or vice versa. See microsoft/TypeScript#9998 for a discussion about this issue.
What it means for the above code: when you register methods with library, you will be able to use them afterward in the same function scope. But you cannot use them "afterward" in some arbitrary other scope like a function body or some other module:
library.registerMethod('somethingElse', () => 123);
function oops() {
library.somethingElse() // the compiler doesn't know about this
}
The compiler really doesn't know that by the time oops() is called, somethingElse will have been registered on library. Actually I don't know it either without examining all of the code everywhere in the program.
A workaround for this would be to do all your registering somewhere, and then "freeze" the type of the resulting library by "saving" it into a new const variable.
const registeredLibrary = library;
function okay() {
registeredLibrary.somethingElse(); // the compiler does know about this
}
That works because registeredLibrary was created as a copy of library in a scope where methods had been registered on it already. There's no place in the code where registeredLibrary fails to have those two extra methods, so the compiler is happy to use them inside the okay() function body.
It is quite possible that the above caveats make this not work for you. The compiler is quite powerful, but unable to handle analysis that needs to happen all at once everywhere for all possible ways the code can run. But assertion methods can at least go some of the way toward modeling this kind of "dynamic" behavior.
Playground link to code
I'm looking for a way to reduce the boilerplate code of my NodeJS backend. In Lombok there is e.g. the possibility to inject constructors and getter/setter by annotation for objects.
Is there a way to do this in TypeScript?
I googled it quickly and found projects like this which attempt to bring Lombok-like capabilities to TypeScript, but as you can see, those project are scarce and not that widely used. That implies a question: Why would you want a tool like that?
TS is already pretty good in reducing boilerplate. When I define a class, I usually do it like this:
class A {
constructor(private fieldA: string, private readonly fieldB = 0) {}
}
This is quite concise, isn't it? I guess you are comparing capabilities of TS to those of Java. Java is very wordy and Lombok helps with that greatly. But TS and JS are different, and while some problems, which Lombok solves, are solved by TS already, others are not an issue in the world of TS and JS.
First of all, the syntax above creates class fields of certain types, with access modifiers and you can also spot the readonly keyword in front of fieldB and its default value 0. On top of that, those are created together with a constructor, which implicitly assigns values to instance fields upon execution (see, there is no this.fieldA = fieldA). So this already more than covers the Lombok's capability to inject constructors. Note on this: In JS (and therefore in TS), you can have only single constructor. JS doesn't support method overloading.
Now about the getters/setters, those are not used the same way in JS (or TS) as they are in Java. In JS, it is a norm that you are working with fields directly, and the setters and getters are used only in special cases where you want to:
Forbid setting a value to an object's property in runtime by defining only a getter. Now this is usually a bit of an overkill, and since you use TS, you can just declare the field as readonly and compiler will make sure you don't assign to that property - no need to use a getter. If you develop in JS without compile time checks, the convention is to mark properties that are private (those you definitely shouldn't modify) with underscore. Either way, it can still happen that you modify a variable that you aren't supposed to modify, but unlike in Java, this is not deemed a reason good enough to use get/set everywhere in JS (and TS). Instead, if you really need to be certain that no modifications happen in runtime, you either use the aforementioned getter without setter, or you configure the object's property as non-writable.
Having a custom logic in set/get functions is the other good reason to employ them. A common use case for this is a getter that is computed out of multiple variables but you still want it to look like an field on an object. That's because in JS, when you invoke a getter, you don't actually use () after the getter name. Now because this logic is custom, it can't be generated just by using an annotation.
So as you can see, some problems Lombok deals with in Java are already dealt with in TS and others are non-issues.
Edit 5-9-2021 - answer to #Reijo's question:
Lomboks functionality goes beyond getters/setters/constructors. Looking at the #Builder Annotation, I am interested in what you would say about this.
If the question is just about whether there is a TypeScript/JavaScript library that offers more or less the same collection of utilities as Lombok for Java, to my knowledge the answer is NO. I think partly it is due to capabilities that TypeScript provides out of the box (as I already outlined above), which brings me back to the point that Java needs Lombok more than languages like TypeScript or Groovy do. When you need something that TS doesn't provide, like the builder pattern, you can use libraries solving a particular problem, like builder-pattern (using JS Proxy in its core) or thanks to the flexible nature of JS (and in effect TS) write it on your own easily.
That's all nice, but you'd perhaps like to add functionality in more declarative way - via annotations (in TS world those are called decorators and they work differently), as Lombok does it. This might prove complex.
First of all, if you modify the type via decorator in TS, TS compiler doesn't recognize the change. So if you augment class by, let's say, adding a method to it in your decorator, TS won't recognize that new method. See this discussion for details.
This means that you either give up on decorators and use functions instead to modify the type (which you can), or you dive into AST. That's btw how Lombok works. It takes annotated types in a compilation phase called annotation processing and thanks to a hack in javac (and Eclipse compiler) modifies their AST (to eg. create an inner builder for given class). One could do it in a somewhat similar way with TS/JS.
Though there is nothing like annotations processing in TS nor JS as such, you could still create a build step that takes a source code and modifies it's AST to achieve your goals (which is how Babel works too). This might result in adding a method to a class, generating a builder etc. based on an annotation (in a broad sense - not necessarily a decorator) used.
This approach is a challenge though. Besides AST being an advanced topic, even if you get it working, you'd need support from your IDE, which nowadays also means support from language servers. And that's not for the faint of heart.
However, my edit is not supposed to scare anyone away if you plan to create something like Lombok for TS, since it seems quite some people would like to see it in TS/JS world. It should only show you what lies ahead ;).
I found an alternative to #Getters and #Setters Lombok decorators.
Try this :
import { capitalize } from "lodash";
const Getters = () => <T extends {new(...args:any[]):{}}>(constructor:T) => {
return class extends constructor {
constructor(...args: any[]) {
super(...args);
const props = Reflect.ownKeys(this);
props.forEach((prop: string) => {
const capitalizedKey = capitalize(prop);
const methodName = `get${capitalizedKey}`;
Object.defineProperty(this, methodName, { value: () => this[prop] });
});
}
}
}
const Setters = () => <T extends {new(...args:any[]):{}}>(constructor:T) => {
return class extends constructor {
constructor(...args: any[]) {
super(...args);
const props = Reflect.ownKeys(this);
props.forEach((prop: string) => {
const capitalizedKey = capitalize(prop);
const methodName = `set${capitalizedKey}`;
Object.defineProperty(this, methodName, { value: (newValue: any) => { this[prop] = newValue } });
});
}
}
}
#Getters()
#Setters()
export class Person {
[x: string]: any;
nom: string;
prenom: string;
constructor(nom: string, prenom: string) {
this.nom = nom;
this.prenom = prenom;
}
}
Say I have the following interface:
interface Example {
optionalString?: string
// ... other props
}
And I have a function taking a Example as a parameter. I know the return type syntax is bogus.
function ensureDefined (example : Example) : Example & {optionalString: string}
The idea of this function is to ensure that the property optionalString of example is defined and that object returned. In which case, every time I wish to access the optionalString property, I don't need to do an if(example.optionalString){}.
I have tried creating an extension of the interface like so:
interface ExampleExtension extends Example {
optionalString: string // notice no '?'
// ... other props
}
But then this one is not assignable back onto a variable with a type of the base interface.
Am I overcomplicating this? Should I just have two separate variables with two different types?
The interface extension works for me on the playground. If it still doesn't work for you, please add code sufficient to reproduce the problem to the question.
What is the best practice for creating model objects in Angular / TypeScript:
Should I use type annotation with object notation (objects are plain instances of Object)? E.g. let m: MyModel = { name: 'foo' }
Should I use the new operator (objects are instances of the respective prototype)?
Should these two approaches be mixed up and used situationally? (E.g plain objects, when receiving a response from the HttpClient, but new MyModel('foobar') for convenience to create instances by passing properties as constructor arguments)
Background of this question
I'm new to TypeScript and like many other developers I come from popular object oriented languages like Java.
A core concept I understood is that type annotations in TypeScript don't play a role at runtime. They are important for the compiler and for your editor's auto completion. Basically it's ECMAScript with the addition of compile time type checks.
At the time I didn't know this and expected TypeScript to be some kind of "client side Java", I found this Angular bug report:
https://github.com/angular/angular/issues/20770
People (who don't understand TypeScript's type concept) complain about the HttpClient not converting the returned plain object to their model class type. Other people are defending this behavior and point out that there are no runtime types in JavaScript.
And here arises my problem: In fact there ARE runtime types in JavaScript. You can create instances of prototypes with the new operator and even check their constructor with the instanceof operator.
In TypeScript you have two ways for creating an instance:
1) Use the object notation (as they show in the Angular tutorial):
hero: Hero = {
id: 1,
name: 'Windstorm'
};
2) Use the new-operator:
hero: Hero = new Hero();
At the moment I'm working on a project where these two options are mixed up. I.e. model objects of the same type are instances of Object in some cases and instances of Hero in other cases.
I expect this leading to problems later, because the constructor is only called in the latter case.
My idea for a rule / best practice was to define all model instances as plain objects and to use the constructor for services, components etc. that are created by dependency injection. As a result I wouldn't use the new operator at all.
However I'm not sure if this is a good idea and I couldn't find a best practice recommendation about it.
EDIT
Important note for close voters: I'm not looking for your personal opionion here. I'm rather looking for some kind of officially documented Angular best practice, as I think it's a core design decision that has to be made right from the project start and these approaches should not be mixed up randomly without a specific reason. Maybe the answer is just a simple "There is no official recommendation which decision to make".
In my opinion, you should use the new operator to create new objects of your class if you need to perform complex operations on the object itself.
If in case you just need to access the properties only, you may use the object literal to create a new object.
This has got advantages like encapsulation, inheritance, etc. which a developer coming from the Java background could better relate to.
If you directly assign an object like below, then you have to explicitly set functions in it, for example, getName function.
hero: Hero = {
id: 1,
name: 'Windstorm',
getName: function(){ // returns name }
};
However, by defining a class Hero with a function getName and then creating an instance of that class, you would automatically get that function, no matter how many times you create instances.
For a better understanding of Object Oriented Javascript, you may follow the link below:-
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS
Regarding the below issue,
"People (who don't understand TypeScript's type concept) complain
about the HttpClient not converting the returned plain object to their
model class type."
HttpClient simply returns an object which the server sends, it is on JS to convert it into the required form. You can always map the response into the required model instance by using its constructor which could be defined as below:-
constructor(hero){
this.id = hero.id;
this.name = hero.name;
}
and you may map the array returned from the server like below:-
map(hero => new Hero(hero))
In fact there ARE runtime types in JavaScript.
Kind of. A value can have a certain type, however variables and properties don't have types bound to them. That said if you type network requests etc.
fetch("someurl").then(res => res.json()) as Promise<IUser>
and that network request suddenly returns something else than a User, nothing will fail as Typescript types do not exist at runtime. However this behaviour can be added easily with typeguards:
function isUser(user: any) : user is IUser {
return Number.isInteger(user.id) && typeof user.name === "string";
}
which allows you to do typechecks at runtime:
const result = await fetch("someurl");
if(!isUser(result)) throw new Error();
// result is definetly a IUser here
Should I use inheritance or not?
That is a question of preference. I once wrote an application that works with the db heavily, so I had to do a lot of serialization / deserialization, and it really annoyed me to always wrap the response from the db into a class instance. So for my db models I started using the following pattern:
type User = {
name: string;
id: number;
};
const User = {
get(id: number): User { /*...*/ }
changeName(user: User, name: string) { /*..*/ }
};
That allowed me to write:
const admin: User = User.get(123);
And I could simply do typecasts on the db and I both had typesafety and nice APIs.
Wether that is a good pattern for your usecase or not really depends on how much serialization / deserialization you do.
This question already has answers here:
Interfaces vs Types in TypeScript
(25 answers)
Closed 6 years ago.
I cannot determine understand when, if ever, you'd want to use a type instead of an interface for a variable in typescript. Assume the following two:
type User = {
id: string;
name: string;
type: string;
}
interface User {
id: string;
name: string;
type: string;
}
I can define a variable with both exactly the same was const user: User = .... However, here are all the things I can do with interface that I cannot do with types:
// Extension:
interface AdminUser extends User {
permissions: string[];
role: string;
}
// Using in abstract method:
abstract class Home {
abstract login(user: User): void;
}
class AdminHome extends Home {
login(user: AdminUser) {
...
}
}
Just to name a few.
So my question is: when would you ever want to use a type?
EDIT (12/15/2020): At the bottom, I've added info on "types" in TS (or the equivalent idea in another language).
Unless I'm mistaken, you're not entirely clear what the purpose of an interface vs. a type is.
In OOP terms, interfaces do not have implementations. Types do. So an interface is basically useless unless a type implements it. Also, a type can only extend one other type. But it can implement many interfaces.
But what's that mean...
Say you have a Car, and a User. Very different types that you don't immediately think of as being the same in any practical way. Some may say, "well, you should create ICar, and IUser". But really, that's just not a practical way to think about interfaces. It would be confusing for User to implement ICar, and/or, ICar just seems to do the same thing as Car. What would be the difference to another programmer looking at the code?
Say you want them both to be (again just for the heck of it), "Self Describable", and you want them both to provide the info the same way. So you'd create:
ISelfDescribable {
getSelfDescription ( );
}
Now, you'd do this:
Car implements ISelfDescribable {
getSelfDescription ( return "I am a car!" );
}
User implements ISelfDescribable {
getSelfDescription ( return ...some other completely different way of getting the info... );
}
An array of these objects would be (think about how else you would do this without interfaces):
Array<ISelfDescribable>
Now you (and any other dev looking at the code) knows for a fact, any object in this array, regardless of the concrete type, implements the "behavior" of ISelfDesribable. If you think about it, there is really no need to EVER know what a type is unless you're implementing it, you just care about the behavior. But you still need the type to implement that behavior.
Say one day you wanted both these objects to be "Insurable". They should both have a method, "setInsurancePolicy". You could create IInsurable { setInsurancePolicy ( policy : Policy ) }, and implement it in the types. Now you have objects that are both ISelfDescribable and IInsurable, and you can type an array of those objects as either one.
So for me, the big lightbulb went off when I got my head around that: types (and type hierarchies) should be concerned with concrete things. Interfaces should be concerned with behaviors that can be shared among different types. There's more to it, but that at least gives a notion of why you'd choose an interface, or a type. They represent different things in terms of programming, even if they otherwise appear the same.
(Addendum: Languages like Scala don't think in terms of interfaces this way. They do have a notion of "behaviors", but you can also implement behaviors, and override them. That may be too much academic blather for this particular question, but hey I need to kill all the monsters in the dungeon, not just the ones for the quest).
12/15/2020: TypeScript: Types vs. Interfaces.
Why use one or the other? Again, it relates to what you want it to represent and how exactly you want to use it.
A "type" is the "I need a contract but it's not a behavior definition like an interface" answer.
This allows you to semantically keep the differentiation. This is an interface, because it defines a horizontal behavior. This is a type, because it defines...a type.
Further along that line though, types are functional-friendly in a way that interfaces are not, because you can combine and intersect types. In React for instance, I've heard it said, "never use interfaces because they are nowhere near as composable as types."
So say you have two service calls, and you want to define a contract for the payloads. Interface? Nah...that's for horizontal behaviors. But a "type", yes...it types the payload but does not semantically define a behavior.
And, say you run into the common task of having to merge the definitions of two payload types into one (for a UI component or something). Using "type" definitions, the language makes that kind of union/intersection/etc straightforward.
So the logic still tracks; interfaces, horizontal behavior, types, vertical definitions that can be composed in a variety of ways.