Typescript implementing interface property - javascript

I have declared interface as below
interface Base {
required: string;
}
I have implemented interface in class like
class MyClass implements Base{
method(): void {
console.log(this.required);
}
}
But I am getting following error
severity: 'Error' message: 'Class 'MyClass' incorrectly implements
interface 'Base'. Property 'required' is missing in type 'MyClass'.'
at: '5,7' source: 'ts'
severity: 'Error' message: 'Property 'required' does not exist on type
'MyClass'.' at: '7,26' source: 'ts'
if I declare required: string; once again in class then no error
interface Base {
required: string;
}
class MyClass implements Base{
required: string;
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
var ss=new MyClass();
ss.method();

If you don't want to delcare requried: string twice use class instate interface for Base and extends instate implements.
class Base {
required: string;
}
class MyClass extends Base{
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
Check it out in the playground.

That's how interfaces work. If you define a property in the interface then you need to define the same property in the class where you are implementing the interface. If you would like to use required property without re define the property, you should create a class an extend it.

Your error is correct. If your class implements an interface, it must also implement all the required properties and methods. If you want not to implement some properties or methods, you can declare them as optional with ? symbol.
interface Base {
required: string;
someProperty?: string; // << look at the symbol `?` in the end of the property
}
Here you can implement the interface and left the someProperty
class MyClass implements Base{
required: string;
// someProperty is missing here, because it is optional
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
And not only you can implement interfaces. Also you can use them as a type. If you have an interface
interface Base {
required: string;
}
you can create an object which is the type of that interface
const obj: Base = { };
But here you will get an error because if your object is of type Base, you need to provide all required properties. So you need to write
const obj: Base = { required: 'Yes' };
This will protect your code from logical errors and your code will be strong typed also for object, for which you don't want to create a class, but you want to said what shape it must be.
Example
You have an interface
interface Name {
name: string
}
and have classes
class Car implements Name {
name: string;
engine: string
constructor(name: string, engine: string){
this.name = name;
this.engine = engine;
}
}
class Person implements Name {
name: string;
surname: string;
constructor(name: string, surname: string){
this.name = name;
this.surname = surname;
}
}
var arr: Name = [new Car('Car', 'JZ'), new Person('Name', 'Surname')];
here arr is an array of type Name. So if you get arr[0], and call on it .engine, intelisense will throw an error that there is no engine property in type Name. But you can be sure, that every object in that array has name property, because the type of that array is Name and it has an required property name.

Related

ReferenceError: Cannot access <Class> before initialization

I Have simple autoloader method that initializes and returns Instance of class using require()
Above simple require is some logic requesting info from database and checking if class exists in filesystem.
let elementClass = require('./elements/' + element.name).default;
Node and ts-node compiles without problem, but when creating instance by require i get
ReferenceError: Cannot access 'Object' before initialization
Object is main abstract class being extended by Element which is then extended by element type.
Required is last in chain class elementType
Class is initialized without problem if there is no Object class being extended, but Object class was tested without any modifications and blank constructor. And all logic transfered to Element class
Object.ts
export default class Object
{
public id: number;
public name: string;
public type: string;
public parent: number;
public owner: number;
public permission: string;
public data: {[name: string]: any};
protected dataSchema: {[name: string]: any} = {};
constructor(id: number, name: string, type: string, parent: number, owner: number, permission: string, data: {[name: string]: any})
{
if(new Validator().validate(data, this.dataSchema))
throw new Error('Invalid input data');
this.id = id;
this.name = name;
this.type = type;
this.parent = parent;
this.owner = owner;
this.permission = permission;
this.data = data;
}
}
Element.ts
import Object from "./object";
export default class Element extends Object
{
/**
* simple logic no constructor
*/
}
ElementType.ts
import Element from '../element';
export default class ElementType extends Element
{
/**
* Simple logic without constructor
*/
}
Even without any simple methods inside classes it doesn't work, it only works when Main Parent class is not used (Object)
Is there some problem requiring class that is twice extended?
Object is reserved word, I just had to rename class to something else.

Typescript complaining about not assigning a get property

I have this code stackblitz
export class Student {
id: number;
name: string;
age?:number;
get studentType():string {
return 'fullTime'
}
constructor(params: Student) {
Object.assign(this, params);
}
}
const student = new Student({id:1, name: 'Jon'}); //ts error here
I get the below error
Argument of type '{ id: number; name: string; }' is not assignable to parameter of type 'Student'.
Property 'studentType' is missing in type '{ id: number; name: string; }'.
While studentType is a get only property and can't bet set.
What is the reason for that and how can I solve it?
ps. (I don't want to make it nullable like studentType? or convert it to just a function)
Getters / Setters are exactly like regular properties, thats why Typescript can't distinguish between a getter / setter and a regular property. You could make all properties optional though, with that you can omit the studentType:
constructor(params: Partial<Student>) {
Object.assign(this, params);
}
However other properties (e.g. name) could also be omitted now. To make that more typesafe you could introduce an additional interface:
export interface StudentData {
id: number;
name: string;
age?:number;
}
export class Student implements StudentData {
get studentType():string {
return 'fullTime'
}
constructor(params: StudentData) {
Object.assign(this, params);
}
}
That is a more controversial topic in TypeScript.
For class, TypeScript consider the overall shape of the class to be the type.
This includes private variables and methods, and in this case, including the getter/setter.
One solution to your problem is you can use Partial<>
constructor(params: Partial<Student>) { ... }
or Pick<>
constructor(params: Pick<Student, 'id' | 'name' | 'age'>) { ... }
Another way is to create an interface yourself:
interface IStudent { id: number, name: string, age?:number }
class Student implements IStudent {
constructor(params: IStudent) { ... }
}
What is the reason for that?
Basically, {id:1, name: 'Jon'} is not a student, since that object lacks a studentType property. This seems obvious and idiotic but makes sense, since typescript cannot know wether you're gonna rely on that property of the argument or not.
In your constructor, you just call Object.assign and let it go. But you could be calling some function that actually relies on the argument having that property, which could led to a runtime error if not pointed out by typescript.
and how can I solve it?
Well, there are several answers already. I would just type the constructor parameter properly. If you expect an object that has id, name and/or age I would type it accordingly:
export class Student {
id: number;
name: string;
age?:number;
get studentType():string {
return 'fullTime'
}
constructor(params: {id: number, name: string, age?: number}) {
Object.assign(this, params);
}
}
const student = new Student({id:1, name: 'Jon'}); //ts error here
This is because you are giving the type in constructor as Student.
This can be done:
export class Student {
id: number;
name: string;
age?: number;
constructor(id:number, name:string,age?:number) {
this.age = age;
this.name = name;
this.id = id;
}
get studentType():string {
return 'fullTime'
}
}
const student = new Student(1, 'Jon');

Does property? mean that the property can be null or undefined on class instances?

For this typescript interface:
export interface Person {
phone?: number;
name?: string;
}
Does this mean that the name property on class instances of the interface can be null or undefined?
For example this post asks a similar question which indicates that it makes the parameter optional. But what does optional mean in the context of an interface? For example suppose I have a class that implements the interface:
class Customer implements Person {
}
Did I now implement Person correctly because all the properties on the Person interface are optional?
Does this mean that the name property on class instances of the interface can be null or undefined?
Yes, it means exactly that.
But what does optional mean in the context of an interface?
It means that you can implement the Interface without setting all the properties.
Here's an example.
interface Person{
name?: string;
age?: number;
}
var b: Person = { name: 'Bob' }; // OK
var c: Person = { name: 'Bob', age: undefined }; // OK
var d: Person = { name: null, age: 'bob' }; // Not OK, age must be a number
OR
class SomePerson implements Person{
public name = 'Bob';
}
OR
class SomeOtherPerson implements Person{
}
Here is the documentation you're looking for.
The advantage of optional properties is that you can describe these possibly available properties while still also preventing use of properties that are not part of the interface.
And yes, you proprerly implemented Person with your Customer class.

Difference between the static and instance sides of classes

I am trying to understand interface topic in Typescript
when I came across Class type, I got this code from official docs
interface ClockConstructor {
new (hour: number, minute: number);
}
class Clock implements ClockConstructor {
currentTime: Date;
constructor(h: number, m: number) { }
}
I can understand that Clock has no match for the signature new (hour: number, minute: number); that's why we get an error there.
But in docs the explaination is something which I am unable to understand. It goes in this way :
This is because when a class implements an interface, only the instance side of the class is checked. Since the constructor sits in the static side, it is not included in this check.
Any explanation would be appreciated.
The interface declares the method/members that the instances have, and not what the implementing class has.
For example check the Array and ArrayConstructor declarations:
interface Array<T> {
length: number;
toString(): string;
toLocaleString(): string;
push(...items: T[]): number;
pop(): T | undefined;
...
[n: number]: T;
}
interface ArrayConstructor {
new (arrayLength?: number): any[];
new <T>(arrayLength: number): T[];
new <T>(...items: T[]): T[];
(arrayLength?: number): any[];
<T>(arrayLength: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): arg is Array<any>;
readonly prototype: Array<any>;
}
As you can see, the Array has method/members which exist on any instance of array:
let a = [];
a.push(1, 2, 3);
console.log(a.length);
But the ArrayConstructor has the members/methods which exist on the Array itself:
console.log(Array. prototype);
console.log(Array.isArray(9));
The constructors are part of the "static" part which is why they are declared in the ArrayConstructor.
If you declare a constructor on an interface for example you'll have a problem implementing that interface:
interface MyInterface {
constructor();
getName(): string;
}
class MyClass implements MyInterface {
constructor() {}
getName() { return "name" };
}
Error:
Class 'MyClass' incorrectly implements interface 'MyInterface'. Types
of property 'constructor' are incompatible. Type 'Function' is not
assignable to type '() => void'. Type 'Function' provides no match for
the signature '(): any'.
Before you can get an instance you need to use the static side, the constructor, to get an instance. You don't need new in your interface anyway, your class is typed itself so typescript knows whatever arguments it has to pass along the constructor.
You can make benefits of an interface with type new if you want to pass a function or class that has to meet certain constructor requirements before it can be instantiated.
interface IFoo {
new(title: string);
}
function MyFunction(ctor: IFoo, title:string) {
return new ctor(title);
}
class MyClass {
constructor(public title: string) {}
}
class MySecondClass {
constructor(public title: string) {}
}
var myClass = MyFunction(MyClass, 'title');
var mySecondClass = MyFunction(MySecondClass, 'title');
console.log(myClass.title, mySecondClass.title);
In fact, a TypeScript class is a regular function in JavaScript which is static when you don't use new in front of it. This is were the docs are referring to.
// static side
function Person() {
}
Person.SayHi = function () {
return 'Hello';
}
console.log(Person.SayHi()); // static function..
var person = new Person() // instance side
See also this answer

Interfaces and Getters\Setters

I have the following:
interface IEngine {
type():string;
type(type:string):void;
}
class Engine implements IEngine {
private _type: string;
get type():string {
return this._type;
}
set type(type:string) {
this._type = type;
}
}
var engine = new Engine();
engine.type = 'foo';
The interface looks to me to be implemented, however, running tsc throws an exception:
F:\>tsc interfaces.ts --target "es5"
interfaces.ts(11,7): error TS2420: Class 'Engine' incorrectly implements interface 'IEngine'.
Types of property 'type' are incompatible.
Type 'string' is not assignable to type '{ (): string; (type: string): void; }'.
You are implementing property, so in interface it should be like this:
interface IEngine {
type:string;
}
Interfaces in Typescript are great for defining the "shape" of the object you want. In your example you are looking for an object that has a property called type. This can be done by specifying:
interface IEngine {
type: string;
}
The getters and setters are implementation details which are then defined in objects that implement the interface such the Engine type in your question.

Categories

Resources