How is it possible to "duck type" an object when the Class has encapsulated properties?

Viewed 147

I was wondering if it is a good practice to use accessors in TS, and is I was testing cases I noticed that doing so, makes you lose the "feature" of "duck-typing".
Is it by design to prevent developers for unwanted mistyping or am I missing something?


    class BlogPost {
        constructor(private _title: string, private _summary: string,
                    private _hoverTitle?: string) { 
        }
        public get title(): string {
            return this._title;
        }
        public set title(value: string) {
            this._title = value;
        }

        public get summary(): string {
            return this._summary;
        }
        public set summary(value: string) {
            this._summary = value;
        }

        public get hoverTitle(): string {
            return this._hoverTitle;
        }
        public set hoverTitle(value: string) {
            this._hoverTitle = value;
        }
    }

    let blogPostItem: BlogPost;

    blogPostItem = {
        hoverTitle = '',
        summary = '',
        title = ''
    }

This should be a valid assignment but it shows me the following error:

Property 'hoverTitle' is missing in type '{ title: string; _title: string; summary: string; _summary: any; }' but required in type 'BlogPost'.

When I try to add these properties , as they are private it shows this error:

Type '{ _hoverTitle: any; _summary: any; _title: any; hoverTitle: any; summary: any; title: any; }' is not assignable to type 'BlogPost'. Property '_title' is private in type 'BlogPost' but not in type '{ _hoverTitle: any; _summary: any; _title: any; hoverTitle: any; summary: any; title: any; }'.

Is this intended or I'm missing something?

Here is a live preview of the error


I think this is a good way to handle the problem! preview

Just have the class implement an interface and then use interface for "duck typing"... this ways doesn't matter if the class has encapsulated private fields because you are typing as the interface.

This came from @Titian Cernicova-Dragomir answer...

2 Answers

Privates make the class behave nominally, it not possible to alias a class with private fields. We can on the other hand remove them by using Pick to get only the public properties:

let blogPostItem: Pick<BlogPost, keyof BlogPost>;

blogPostItem = {
    hoverTitle: '',
    summary: '',
    title: ''
}

You can also use a type assertion to ignore the errors.

To amend @Titian Cernicova-Dragomir answer,

Privates make the class behave nominally.

Notice he says "behave nominally", but not "nominal".

the type of Class in TypeScript includes private fields and compare them structurally. It is a design choice made by the TS team to avoid accidental access or overriding the private fields.

It does create a lot of confusion and pain points, and one of the main driving force for myself to do class-less programming in TS.

The Pick<> is a nice trick to get around this "limitation".

Related