typescript - Como é possível "duck type" um objeto quando a Classe possui propriedades encapsuladas?
Eu estava me perguntando se é uma boa prática usar accessors em TS, e enquanto eu testava casos, percebi que fazer isso faz você perder o "recurso" de "duck-typing".
Isso é por design para impedir que desenvolvedores cometam erros de digitação indesejados ou estou perdendo alguma coisa?
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 = ''
}
Isso deveria ser uma atribuição válida, mas ele me mostra o seguinte erro:
Propriedade 'hoverTitle' está ausente no tipo '{ title: string; _title: string; summary: string; _summary: any; }', mas é obrigatória no tipo 'BlogPost'.
Quando tento adicionar essas propriedades, como elas são privadas, aparece este erro:
Tipo '{ _hoverTitle: any; _summary: any; _title: any; hoverTitle: any; summary: any; title: any; }' não é atribuível ao tipo 'BlogPost'. Propriedade '_title' é privada no tipo 'BlogPost', mas não no tipo '{ _hoverTitle: any; _summary: any; _title: any; hoverTitle: any; summary: any; title: any; }'.
Isso é intencional ou estou perdendo algo?
Aqui está uma visualização ao vivo do erro
Eu acho que esta é uma boa maneira de lidar com o problema! visualização
Basta fazer a classe implementar uma interface e então usar a interface para "duck typing"... desta forma não importa se a classe tem campos privados encapsulados porque você está digitando como a interface.
Isso veio da resposta de @Titian Cernicova-Dragomir...
Resposta #1
Privates fazem a classe se comportar de forma nominal, não é possível criar um alias de uma classe com campos privados. Por outro lado, podemos removê-los usando Pick para obter apenas as propriedades públicas:
let blogPostItem: Pick<BlogPost, keyof BlogPost>;
blogPostItem = {
hoverTitle: '',
summary: '',
title: ''
}
Você também pode usar uma declaração de tipo para ignorar os erros.
Resposta #2
Para emendar a resposta de @Titian Cernicova-Dragomir,
Os soldados fazem a classe se comportar normalmente.
Note que ele diz "comporte-se nominalmente", mas não "nominal".
O tipo de Classe no TypeScript inclui campos privados e os compara estruturalmente. É uma escolha de design feita pela equipe do TS para evitar acesso acidental ou substituição dos campos privados.
Isso realmente cria muita confusão e pontos de dor, e uma das principais forças motivadoras para mim fazer programação sem classes em TS.
O Pick<> é um truque legal para contornar essa "limitação".
