Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`Partial<this['someProperty']>` not working in typescript

Tags:

typescript

Given

class A {
    props: {
        bool?: boolean,
        test: string
    } = {
        test: 'a'
    };
    setProps(newProps: Partial<this['props']>) {

    }
    a() {
        this.setProps({
            bool: false
        });
    }
}

I get an error

Argument of type { bool: false; } is not assignable to parameter of type Partial<this["props"]>.(2345)

As can be viewed here: same code in typescript playground

When the A.props property is defined as {bool?: boolean, test?: string} with test optional it does work, so it looks as if the Partial<> is just completely ignored.

Is there a way to get the Partial to work in conjunction with this.


The above describes the minimal case, for context: My current situation is that I have lots of classes with props that extends from a main class that has a setProps and I would like to type that inherited setProps.

class Parent {
    props: any;
    setProps(newProps: Partial<this['props']>) {/*...*/}
}
class A extends Parent{
    props: {
        bool?: boolean,
        test: string
    } = /* ... */;
    a() {
        this.setProps({
            bool: false
        });
    }
}
like image 767
David Mulder Avatar asked Dec 04 '20 10:12

David Mulder


1 Answers

Edit #2: Maybe another approach is more suitable for you. The following code implements the class Parent as an generic one receiving the interface of the properties (here PropsA) of an extending class (here A).

class Parent<T> {

    props: T;

    constructor(props: T) {
        this.props = props;
    }

    setProps(props: Partial<T>) {
        Object.assign(this.props, props);
    }
}

interface PropsA {
    bool?: boolean;
    test: string;
}

class A extends Parent<PropsA> {

    constructor() {
        super({ test: 'A' });
    }

    a() {
        this.setProps({
            bool: false,
            // Yields error:
            // nonExistingProperty: true
        });
    }
}

const obj = new A();
console.log(obj.props); // { test: 'a' }

obj.setProps({ test: 'foo', bool: true });
console.log(obj.props); // { test: 'foo', bool: true }

obj.setProps({ test: 'foo' });
console.log(obj.props); // { test: 'foo', bool: true }

obj.a();
console.log(obj.props); // { test: 'foo', bool: false }

// Yields error:
// obj.setProps({ test: 'foo', nonExistingProperty: true });

Instead of an interface, something like class A extends Parent<{ bool?: boolean; test: string;}> { ... would also do the job. However I think, that an interface makes more sense here.

Edit #1: I have experimented a bit with ThisType. However I am not sure about its benefit, but maybe it is worth to mention:

type Descriptor<T, P> = ThisType<T> & Partial<P>;

class A {
    props: {
        bool?: boolean,
        test: string
    } = {
        test: 'a'
    };
    setProps<T = Descriptor<this, this['props']>>(newProps: T) {
        Object.assign(this.props, newProps);
    }
    a() {
        this.setProps({
            bool: true
        });
    }
}

const obj = new A();
console.log(obj.props); // { test: 'a' }

obj.setProps({ test: 'foo', bool: true });
console.log(obj.props); // { test: 'foo', bool: true }

Original answer: Try Partial<A['props']>:

class A {
    props: {
        bool?: boolean,
        test: string
    } = {
        test: 'a'
    };
    setProps(newProps: Partial<A['props']>) {
        // First naive assignment
        Object.assign(this.props, newProps);
    }
    a() {
        this.setProps({
            bool: false
        });
    }
}

I guess the error appears because of this.

like image 99
pzaenger Avatar answered Oct 26 '22 20:10

pzaenger