user9124444
user9124444

Reputation:

Typescript Generics constraint not working

Hi guys so I have this problem

I'm creating a service that i only want to use with a constraint

for that I have an interface

abstract class Base {

}


class ModelFromBase extends Base {

}

interface IService<T extends Base> { 
   doSomething(model: T); 
}

and this class that implements the interface

class ModelNotFromBase {

}

class Services<ModelNotFromBase>  implements IService<ModelNotFromBase> {
       doSomething(model: T) {} 
}

The issue is that ModelNotFromBase does not extends Base (as name suggests), and the TS compiler does not scream about it.

Also when I'm creating an IService instance

private _instance: IService<ModelNotFromBase>;

same issue, I mean i could provide anything there.

So what is the issue here,

As the documentation shows you can use constraints

https://www.typescriptlang.org/docs/handbook/generics.html

Is there something that I'm missing out ? or am I not doing it the correct way ?

Upvotes: 1

Views: 634

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249476

Depending on how Base and ModelNotFromBase are defined, this may be the expected behavior. When determining type compatibility, typescript uses structural compatibility. This means that these two classes are perfectly compatible with one another:

class Base {
    name: string;
}

class ModelNotFromBase {
    name: string;
}
var b : Base = new ModelNotFromBase(); // Works fine 

If the class/interface Base is empty (class Base { }), then the constraint, does not really constrain anything as any other type can be assign a variable of this type

class Base { }
var d : Base = 0;
var d2: Base = "";

If you want to avoid structural typing, you can define a private property on the Base class (it does not have to be used it just has to be present) and then constraint can only be satisfied by a class that inherits Base:

class Base { 
    private nonstructural: true;
}
class ModelNotFromBase { }

interface ITemplatingService<T extends Base> {
    doSomething(model: T);
}

export class Services<T extends Base> implements ITemplatingService<T> {
    doSomething(model: T) { }
}

var  _instance: ITemplatingService<ModelNotFromBase>; //error 
export class OtherServices implements ITemplatingService<ModelNotFromBase> { // also error
    doSomething(model: ModelNotFromBase) { }
}

Upvotes: 1

Related Questions