'Interface must contain only keys from an array literal
In an HTTP response we have a bunch of keyed sections that get grouped when received in the client and I need to filter based on specific keys in order to group.
I was hoping I could have it so the addition of an item to a specific group would be enforceable on the interface or equivalent type so it isn't overlooked and provides a bit of assistance to someone who probably didn't read the comment.
I've been playing with this on a specific group for profile sections and going back and forth on how it might be implemented, but ultimately thinking it might not be possible:
export interface Section {
statusCode: number;
}
export interface CustomSection extends Section {
differentKey: string;
}
export const profileSectionKeys = [
'demographics',
'collegeCertification',
'userAccessAgreement',
'transactions',
'newSection' // <--- newly added section to the profile group
] as const;
export type ProfileSectionKey = typeof profileSectionKeys[number];
export type IProfileSection = {
[K in typeof profileSectionKeys[number]]: Section;
};
export interface ProfileSection { // <--- want to have type error when key is missing
demographics: CustomSection;
collegeCertification: CustomSection;
userAccessAgreement: Section;
transactions: Section;
// Missing newSection key!
}
Solution 1:[1]
By far the easiest thing you could do is just make ProfileSection
an interface
that extends
IProfileSection
:
interface ProfileSection extends IProfileSection {
demographics: CustomSection;
collegeCertification: CustomSection;
}
This automatically inherits the properties from IProfileSection
into ProfileSection
, and if you want to make some of the properties more specific, you can narrow them by redeclaring them, as shown. So if you don't mention a property, it will just be a Section
:
declare const profileSection: ProfileSection;
profileSection.demographics.differentKey.toUpperCase(); // okay
profileSection.userAccessAgreement.statusCode.toFixed(); // also okay
profileSection.newSection.statusCode.toFixed(); // also okay
So there's no chance of accidentally "leaving out" newSection
.
On the other hand, maybe you have a use case whereby you don't want ProfileSection
to automatically inherit from IProfileSection
, but you do want to make sure that it is structurally compatible with IProfileSection
... so it should not leave out any keys and all the properties must be of type Section
or something narrower. (I can't actually think of such a use case, but this is what you've asked for, so ????).
If so, then I don't know of any way to annotate the declaration of ProfileSection
itself to achieve this. But you can make a "helper" type that checks ProfileSection
and provides the requisite error if that constraint is violated. For example:
type CheckProfileSection<T extends IProfileSection =
ProfileSection> = void
// ^^^^^^^^^^^^^^ <-- if there's an error here then you need to fix ProfileSection below
interface ProfileSection {
demographics: CustomSection;
collegeCertification: CustomSection;
userAccessAgreement: Section;
transactions: Section;
newSection: Section;
}
Nobody will ever use CheckProfileSection
for anything, (it evaluates to void
no matter what). Its only purpose is to "watch" ProfileSection
. The above compiles without error. But if I remove newSection
from ProfileSection
,
interface ProfileSection {
demographics: CustomSection;
collegeCertification: CustomSection;
userAccessAgreement: Section;
transactions: Section;
// newSection: Section;
}
then you get this error:
type CheckProfileSection<T extends IProfileSection =
ProfileSection> = void // error!!
// ~~~~~~~~~~~~~~
// Property 'newSection' is missing in type 'ProfileSection'
// but required in type 'IProfileSection'.
So that works as desired.
There you go. Personally I would just write interface ProfileSection extends IProfileSection
, but the CheckProfileSection
option also works.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | jcalz |