0

I'm trying to enforce to pass an object with properties existing in a derived class in the following way:


class Observable<Event> {    // A simple implementation of the observer pattern
    notify( p: Event ) {
        this.callback(p)
    }

    callback: ( p: Event ) => void
}

// the event will be an object with keys on the Observable class
type ChangeEvent<T> = {    
    [ P in keyof T ]?: T[P]
}

class Base {
    changeAndNotify<P extends keyof this>( key: P, value: this[P]) {
        this[key] = value
        this.observable.notify({ [key]: value })   // this works
    }

    observable: Observable<ChangeEvent<Base>>  // I tried this instead of Base but does not work
}

class Derived extends Base {
    doSomething() {
        this.changeAndNotify( 'name', 'Jane' )    // this works
        this.observable.notify({ name: 'Jane' })  // this produces an error
    }

    name: string
}

I want the Derived class properties to be accepted by the notify method but only properties from Base class ara valid.

Any sugestion?

jseto
  • 51
  • 7
  • Hmm, what you are asking for is unsafe; you can have `class X extends Derived { name: "John" }` where the `name` field is [narrower than `string`](https://www.typescriptlang.org/docs/handbook/literal-types.html#string-literal-types). Then the `doSomething()` method of `X` would illegally call `this.observable.notify({name: "Jane"})` whereas only `{name: "John"}` should be accepted. There's no plausible way to produce a value of type `Partial` inside the implementation of a class; see [this](https://stackoverflow.com/questions/46980763/why-cant-i-return-a-generic-t-to-satisfy-a-partialt). – jcalz Jan 21 '21 at 02:12
  • I'm not sure what to say you should do instead. You could always use a type assertion, like [this code](https://tsplay.dev/4w1R8N), to force `{name: "Jane"}` to be accepted. Using `this[P]` is sort of the same thing as a type assertion because it unsafely widens `this` to `Base` to do the property access. Does that work for you? If so I'll write up an answer with the caveat that some possible subclasses of `Derived` could violate your intended constraints. – jcalz Jan 21 '21 at 02:22
  • @jcalz I'm not trying to narrow name's type. Name is a string and can take any string value. What I want is notify to accept as parameter an object with key 'name' which is declared in a derived class. – jseto Jan 23 '21 at 19:02
  • In subclasses, `name` may indeed be narrowed, whether you want that or not. That’s why the compiler complains and you can’t prevent that if you want to use polymorphic `this`. If you want to prevent that error you can use a type assertion, as in the code I linked. If that does not suffice I will refrain from posting an answer, unless you want an answer that says “this is impossible, sorry”. – jcalz Jan 23 '21 at 20:03
  • Just wondering why I need TS typing if I have to use `any` to type. – jseto Jan 24 '21 at 18:48

0 Answers0