Implementation of interface requires member's redeclaration in a class [duplicate] - javascript

Hello TypeScript experts.
I have the following code but I have to repeat the interface properties in the class otherwise I get:
Class incorrectly implements interface
When using an interface, is there a TypeScript shorthand for doing this without having to declare Id: number; and all the other properties in the class? Thx
interface INavigation {
Id: number;
AppId: number;
NavId: number;
Name: string;
ParentId: string;
PageURL: string;
Position: string;
Active: string;
Desktop: string;
Tablet: string;
Phone: string;
RoleId: string;
Target: string;
}
class Navigation implements INavigation {
Id: number;
AppId: number;
NavId: number;
Name: string;
ParentId: string;
PageURL: string;
Position: string;
Active: string;
Desktop: string;
Tablet: string;
Phone: string;
RoleId: string;
Target: string;
constructor(navigation: any) {
this.Id = navigation.Id
this.AppId = navigation.NavAppId
this.NavId = navigation.NavId
this.Name = navigation.NavName
this.ParentId = navigation.NavParentId
this.PageURL = navigation.NavPageURL
this.Position = navigation.NavPosition
this.Active = navigation.NavActive
this.Desktop = navigation.NavDesktop
this.Tablet = navigation.NavTablet
this.Phone = navigation.NavPhone
this.RoleId = navigation.NavRoleId
this.Target = navigation.NavTarget
}
}

This is now possible in Typescript using class/interface merging.
interface Foo {
a: number;
}
interface Baz extends Foo { }
class Baz {
constructor() {
console.log(this.a); // no error here
}
}
https://github.com/Microsoft/TypeScript/issues/340#issuecomment-184964440

There is no built-in support for this.
We can however use a function that returns a class as the base type of our class. This function can lie a little bit and claim it implements the interface. We could also pass in some defaults for the members if necessary.
interface INavigation {
Id: number;
AppId: number;
NavId: number;
Name: string;
ParentId: string;
PageURL: string;
Position: string;
Active: string;
Desktop: string;
Tablet: string;
Phone: string;
RoleId: string;
Target: string;
}
function autoImplement<T>(defaults?: Partial<T>) {
return class {
constructor() {
Object.assign(this, defaults || {});
}
} as new () => T
}
class Navigation extends autoImplement<INavigation>() {
constructor(navigation: any) {
super();
// other init here
}
}
If we want to have a base class, things get a bit more complicated since we have to perform a bit of surgery on the base type:
function autoImplementWithBase<TBase extends new (...args: any[]) => any>(base: TBase) {
return function <T>(defaults?: Partial<T>): Pick<TBase, keyof TBase> & {
new(...a: (TBase extends new (...o: infer A) => unknown ? A : [])): InstanceType<TBase> & T
} {
return class extends base {
constructor(...a: any[]) {
super(...a);
Object.assign(this, defaults || {});
}
} as any
}
}
class BaseClass {
m() { }
foo: string
static staticM() { }
static staticFoo: string
}
class Navigation extends autoImplementWithBase(BaseClass)<INavigation>() {
constructor(navigation: any) {
super();
// Other init here
}
}
Navigation.staticFoo
Navigation.staticM
new Navigation(null).m();
new Navigation(null).foo;

A mix between the 2 answers, for avoid long assignment in the constructor by using class/interface merging and Object.assign in the constructor :
interface Foo {
a: number;
b: number;
}
interface Baz extends Foo { }
class Baz {
c: number = 4
constructor (foo: Foo) {
Object.assign(this, foo, {})
}
getC() {
return this.c
}
}
let foo: Foo = {
a: 3,
b: 8
}
let baz = new Baz(foo)
// keep methods and properties
console.warn(baz)

in case you using angular you can pass whole component as prop
which is crazy 😜
Child Component TS:
#Input() parent!: YourParentClass;
Parent Component HTML:
<child [parent]="this" ></child>

Class declarations should explicitly implement interfaces.

Related

Angular showing error: Type '{ }[]' is not assignable to type '[{ }]'

I need some help and explanation with the error I am receiving in my app... I get a JSON from an API that gives me some data and that data has an array products. On click I want to copy these products(izdelki) from this array to a new empty array and send it over an API call to the backend.
But I have a problem with getting the products from this array I receive. My code is returning me this error:
error TS2322: Type '{ sifra: string; naziv: string; kolicina: number; ean: string; em: string; cena: number; rabat1: number; rabat2: number; prednarocilo: number; ismail: number; }[]' is not assignable to type '[{ sifra: string; naziv: string; kolicina: number; ean: string; em: string; cena: number; rabat1: number; rabat2: number; prednarocilo: number; ismail: number; }]'.
[ng] Target requires 1 element(s) but source may have fewer.
[ng]
[ng] 38 this.orderProducts = data.map(this.order['izdelki']);
I am new to angular and Arrays are giving me some trouble :)
single-order.ts code:
export interface SingleOrder {
id: number;
datum: string;
datum_dobave: string;
dostava: number;
g_popust: number;
opomba: string;
predkoci1narocilo: number;
kc: number;
prevoznik: string;
narocilnica: string;
narocilnicadate: string;
izdelki: {
sifra: string;
naziv: string;
kolicina: number;
ean: string;
em: string;
cena: number;
rabat1: number;
rabat2: number;
prednarocilo: number;
ismail: number;
}[];
}
Service to get the single order:
getSingleOrder(id: number): Observable<SingleOrder[]> {
return from(Preferences.get({ key: 'TOKEN_KEY' })).pipe(
switchMap(token => {
const headers = new HttpHeaders().set('Authorization', `Bearer ${token.value}`);
return this.httpClient.get<SingleOrder[]>(`${environment.apiUrl}customer/orders/${id}`, { headers, observe: 'response' });
}),
catchError(err => {
console.log(err.status);
if (err.status === 400) {
console.log(err.error.message);
}
if (err.status === 401) {
this.authService.logout();
this.router.navigateByUrl('/login', { replaceUrl: true });
}
return EMPTY;
}),
map(res => res.body)
);
};
Here is my order-view.page.ts code:
export class Izdelki {
sifra: string;
naziv: string;
kolicina: number;
ean: string;
em: string;
cena: number;
rabat1: number;
rabat2: number;
prednarocilo: number;
ismail: number;
}
#Component({
selector: 'app-order-view',
templateUrl: './order-view.page.html',
styleUrls: ['./order-view.page.scss'],
})
export class OrderViewPage implements OnInit, OnDestroy {
order: SingleOrder[];
// orderProducts: SingleOrder['izdelki'][];
orderProducts: SingleOrder['izdelki'][];
repeatOrderArr: Izdelki[];
private orderSubscription: Subscription;
constructor(
private route: ActivatedRoute,
private customerService: CustomerService,
) { }
ngOnInit() {
this.getOrder();
}
getOrder() {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.orderSubscription = this.customerService.getSingleOrder(id).subscribe(
data => {
this.order = data;
console.log('Order data:', this.order);
this.orderProducts = data.map(this.order['izdelki']);
},
error => {
console.log('Error', error);
});
}
repeatThisPurchase() {
this.repeatOrderArr= [...this.orderProducts];
console.log(this.repeatOrderArr);
}
ngOnDestroy(): void{
this.orderSubscription.unsubscribe();
}
}
Here is an image of console.log(data) so you can see whats inside the JSON response:
HTML file code:
<ion-button color="vigros" class="purchase-btn" size="default" type="submit" (click)="repeatThisPurchase()" expand="block">Ponovi nakup</ion-button>
let say izdelki is a class
export class Izdelki {
sifra: string;
naziv: string;
kolicina: number;
ean: string;
em: string;
cena: number;
rabat1: number;
rabat2: number;
prednarocilo: number;
ismail: number;
}
so inSingleORder you declared izdelki with type [Izdelki]
export interface SingleOrder {
izdelki: [Izdelki]
}
but in your subscribe you used it directly like if izdelki is of type Izdelki
So SingleOrder izdelki became
export interface SingleOrder {
izdelki: Izdelki
}
or if izdelki is an array
export interface SingleOrder {
izdelki: Izdelki[]
}
To solve your issue you have to
declare SingleOrder izdelki with the type Izdelki
declare orderProducts as an array of SingleOrder['izdelki']
orderProducts: SingleOrder['izdelki'][];
You have inversed the declaration of your array. Start by declaring the type of the array fist, like :
export interface SingleOrder {
...
izdelki: {
sifra: string;
naziv: string;
kolicina: number;
ean: string;
em: string;
cena: number;
rabat1: number;
rabat2: number;
prednarocilo: number;
ismail: number;
}[];
}

How to use Typescript and Interfaces when passing props down through React components?

I'm passing down a pretty large array of objects through several React components. I was wondering, what is the syntax for writing the types of all the properties in each object (the objects are nested several times)?
I currently have interfaces like below. These are two components, MainContent, which passes props down into Chart:
MainContent component:
interface ComparatorTypes {
id: string;
name: string;
}
interface DataTypes {
jobId: string;
jobTitle: string;
descriptionUrl: string;
totalCompensation: number;
baseSalary: number;
longevityPay: number;
specialPay: number;
allowances: number;
paidTimeOff: number;
holidays: number;
retirementBenefit: Array<{
formula: string;
details: any;
}>;
healthBenefit: Array<{
premium: number;
details: any;
}>;
remoteWork: {
isAllowed: string;
details: any;
};
}
interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
interface params {
comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}
export default function MainContent({ comparatorData, queryData }: params) {
return (
<S.MainContentComponent>
<Header />
<Summary comparatorData={comparatorData} />
<Chart queryData={queryData} />
</S.MainContentComponent>
);
}
and Chart component:
interface ComparatorTypes {
id: string;
name: string;
}
interface DataTypes {
jobId: string;
jobTitle: string;
descriptionUrl: string;
totalCompensation: number;
baseSalary: number;
longevityPay: number;
specialPay: number;
allowances: number;
paidTimeOff: number;
holidays: number;
retirementBenefit: Array<{
formula: string;
details: any;
}>;
healthBenefit: Array<{
premium: number;
details: any;
}>;
remoteWork: {
isAllowed: string;
details: any;
};
}
interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
interface params {
// comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}
export default function Chart({ queryData }: params): JSX.Element {
...
You can see how redundant it is to be naming these giant, several-times-nested interfaces before every component that uses this array of objects. Is this normal for Typescript? Is there a better way to do something like this? Or does all this data need to be typed upon being passed down through every component?
What forces you to define these identical interfaces explictly for each component?
On the contrary, factorizing them would be the normal choice: that way, they are defined in a single place (single source of truth), and by importing them, you explictly say that you re-use the exact same types.
// Chart.tsx
export interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
export interface DataTypes {
jobId: string;
jobTitle: string;
// etc.
}
export default function Chart({
queryData
}: {
queryData: QueryTypes[];
}) {}
// Main.tsx
import Chart, { QueryTypes } from ".Chart";
import Summary, { ComparatorTypes } from "./Summary"; // Same for ComparatorTypes
export default function MainContent({
comparatorData,
queryData
}: {
comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}) {
return (
<S.MainContentComponent>
<Header />
<Summary comparatorData={comparatorData} />
<Chart queryData={queryData} />
</S.MainContentComponent>
);
}

React Typescript interface

I get a problem when making an interface on Typescript!
export interface Content {
uuid?: string;
brand_id?: string;
brand_name?: string;
is_active?: boolean;
}
export interface Pageable {
sort?: Sort;
pageSize?: number;
pageNumber?: number;
offset?: number;
unpaged?: boolean;
paged?: boolean;
}
export interface Sort {
sorted?: boolean;
unsorted?: boolean;
empty?: boolean;
}
export interface BrandData {
content?: Content;
pageable?: Pageable;
numberOfElements?: number;
number?: number;
sort?: Sort;
size?: number;
first?: boolean;
last?: boolean;
empty?: boolean;
}
export interface BrandResponse {
readonly data?: BrandData[];
}
and then i want to get data in react component like this
const { brands } = useSelector((state: AppState) => state.catalogs.brands);
const [brandData, setBrandData] = React.useState(brands.data);
const [currentPage, setCurrentPage] = React.useState(brandData.pageable.pageNumber);
then the error exits like this
Property 'pageable' does not exist on type 'BrandData[]'.

Extending Interface/types in typescript

I have extended my FilterMetaData type to add a dynamic model which is different structure for different entities but somehow typescript not recognizing my object properties. I might be missing something here.
Interfaces
export type FilterMetaData<T extends {}> = T & {
filterCriteriaID: number;
filterCriteriaName?: string;
isDefaultSelected?: boolean;
isExpanded?: boolean;
}
export interface DateRange {
data?: DateRangeData;
}
export interface DateRangeData {
min?: Date;
max?: Date;
}
export interface Range {
data?: RangeData;
}
export interface RangeData {
currency?: string;
timePeriod?: string;
min?: number;
max?: number;
step?: number;
}
export interface Select {
data?: SelectData[];
}
export interface SelectData {
id?: number;
name?: string;
isSelected?:boolean|false;
parentID?: number | null;
parentName?: string;
}
Implementation in class
helper<T1 extends FilterMetaData<T2>>(filter) {
this.selectedFilterService.getAppliedFilterMasterData<T1>(filter)
.pipe(takeUntil(this.unsubscribe$))
.subscribe((filterData: T1) => {
if (filter.isDefaultSelected) {
filterData.isExpanded = false;
filterData.data.forEach((data) => data.isSelected = false);
this.selectedFilters.result.select.push(filterData);
this.loadComponent(new FilterComponentType(SelectComponent, filterData));
} else {
this.removeComponent(filter);
}
}, err => {
this.toastrService.error(err);
})
}
Invoking Function with type args like this:--
this.helper<FilterMetaData<Select>>(filter);

Cannot set property of Interface in TypeScript

I have create a simple interface
module Modules.Part
{
export interface IPart
{
PartId: number;
partNumber: string;
description: string;
}
}
then i have declare this interface in another interface
module Interfaces.Scopes {
export interface IPartScope extends ng.IScope {
part: Modules.Part.IPart;
vm: Controllers.PartCtrl;
}
}
i have use this interface in my class
module Controllers
{
export class PartCtrl
{
constructor(public scope:Interfaces.Scopes.IPartScope)
{
scope.part.PartId = 1;
scope.part.partNumber = "123part";
scope.part.description = "description";
}
}
}
when i am going to set property of IPart interface in my class it's give me following error
TypeError: Cannot set property 'PartId' of undefined
please let me know how to solve this
I'd say, you first need to initialize the part property of the PartCtrl.scope property (which you implicitly defining through the constructor):
constructor(public scope:Interfaces.Scopes.IPartScope)
{
scope.part = [initializer];
scope.part.PartId = 1;
scope.part.partNumber = "123part";
scope.part.description = "description";
}
Since the scope.part is of interface type IPart, you can't create an instance of it but rather have to resolve it somehow. For example:
export interface IActivator<T> {
new (): T;
}
export class PartCtrl<TPart extends IPart>
{
constructor(public scope:Interfaces.Scopes.IPartScope,
activator: IActivator<TPart>)
{
scope.part = new activator();
// ...
}
}
Now you can use the PartCtrl<TPart> in the following way:
export class ConcretePart implements IPart
{
public PartId: number;
public partNumber: string;
public description: string;
}
var ctrl = new PartCtrl(ConcretePart);
Hope this helps.
You can initialize the property "part" with an anonymous object.
scope.part =
{
PartId = 1;
partNumber = "123part";
description = "description";
}
Of course you can also define a class that implements IPart and instantiate it.

Categories

Resources