Based on the following structure:
interface ApiEntity {
[key: string]: number | string;
}
interface ApiData {
[key: string]: ApiEntity;
}
interface Entity1 extends ApiEntity {
id: number;
name: string;
}
interface Entity2 extends ApiEntity {
uuid: string;
description: string;
}
interface MyData extends ApiData {
a: Entity1;
b: Entity2;
}
How can I create an interface that would accept only a valid entity and property:
// The problem
interface DataFields<T extends ApiData> {
label: string;
entity: keyof T; // ensure that entity is one of the properites of abstract ApiData
property: keyof keyof T; // ensure that property is one of the properties of ApiEntity
other?: string;
}
So the created fields are safe and TS shows errors when invalid:
const fields: MyDataFields<MyData>[] = [{
label: 'A ID',
entity: 'a', // valid
property: 'id', // valid
},{
label: 'B Description',
entity: 'b', // valid
property: 'description', // valid
},{
label: 'Invalid',
entity: 'c', // TS Error
property: 'name', // TS Error
}];
Or even better:
const MyDataFields: DataField<MyData>[] = [
{label: 'A ID', entityProperty: 'a.id'},
{label: 'B Description', entityProperty: 'b.description'},
{label: 'Invalid', entityProperty: 'c.name'}, // TS Error
];
ApiDatais declared to allow any key as a string, andMyDataextendsApiData, so how cancbe inferred as an invalid property name? The interface says every string is a valid property name. The same applies forEntity1andEntitywhich extendApiEntity, where the latter says every string is a valid property name.