0
votes

Generic interfaces and classes, I like them and I think they are very useful . I have just started using them and I would like to understand why I am having the 2 errors I have in the TS playground (link). I would appreciate any help, THANKS in advance.

Issue 1: returned variable o (Person.fromJson(j)) implements IPerson<T,K> and T is a extension of IPerson<T,K> so I don't understand why is wrong. I did a basic use case with no generic classes at the end of the TS playground and I don't see the same error.

Description error: in the return o of Person.fromJson(j)

Type 'Person<IPerson<unknown, JsonPerson>, JsonPerson>' is not assignable to type 'T'.

'T' could be instantiated with an arbitrary type which could be unrelated to 'Person<IPerson<unknown, JsonPerson>, JsonPerson>'

class Person<T extends IPerson<T,K>, K extends JsonPerson> implements IPerson<T,K>{
    name: string = "Unknown"
    constructor(){}
    toJson(): JsonPerson{
         return {"name": this.name}
    }
    fromJson(j: K): T{
         let o= new Person()
         o.name = j.name
         return o
    }
}

Issue 2: The error states "Type T does not satisfy....", but if T extends IStudent and IStudent extends IPerson. So T "extends IPerson"? I think that yes (inclusion principle). So that is why I don't understand the next error. Maybe it refers that it does not satisfy due to fromJson incompatibility but not sure how to solve it. Maybe this comes from Issue 1.

Description error: in extends Person<T,K>

Type 'T' does not satisfy the constraint 'IPerson<T, K>'. Type 'IStudent' is not assignable to type 'IPerson<T, K>'. The types returned by 'fromJson(...)' are incompatible between these types. Type 'IStudent' is not assignable to type 'T'. 'IStudent' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'IStudent'.

class Student<T extends IStudent, K extends JsonStudent> extends Person<T,K> 

implements IStudent{
    student: boolean= true
    constructor(){
        super()
    }
    toJson():JsonStudent{
        return {...super.toJson(), "student":this.student}
    }

    fromJson(j: K): T{
        let o: T = super.fromJson(j)
        o.student=j.student
        return o 
    }
}
1

1 Answers

0
votes

You've got a lot of variables in this code and it could be cleaned up and simplified quite a bit.

The core of your problem is what happens with the fromJson method when you are dealing with a type that is more specific than what you expected.

All students are people, but not all people are students. You have said that T extends IPerson<T,K> and that keyword extends means that T can include any additional properties. Let's say that you create a Person instance and specify that the T for this instance is your expected person object but also {customProperty: any}. When you call fromJson you'll get a Person object which does not include customProperty, so it's not assignable to T.

This is why you get errors like "'T' could be instantiated with an arbitrary type which could be unrelated to ___" and "'T' could be instantiated with a different subtype of constraint ___".