Reputation: 2290
I have a class Form
that has this signature:
interface IFormSchema {
name: string;
// Removed irrelevant fields...
}
const schema: IFormSchema[] = [{ name: 'firstName' }];
// Form Interface
interface IForm {
fields: IFields;
// Removed irrelevant fields...
}
// Here is the IFields interface used in IForm
interface IFields {
// The key here is what I want typed **************
[key: string]: IField;
}
const form: IForm = new Form(schema: IFormSchema[]);
The schema array is iterated and each object is converted to a Field
with the IField
interface:
interface IField {
form: IForm;
name: string;
// Removed irrelevant fields...
}
Now, when I new up the Form
and then I access the Form.fields
, and can access the field by its name like so form.fields.firstName
, I want firstName
to be typed so that if I try and access form.fields.wrongFieldName
, TypeScript will throw an error.
How would I do this? Any help would be greatly appreciated.
Upvotes: 0
Views: 2298
Reputation: 51034
This is possible, but you need to use generic types and type inference. If you have an explicit type annotation like : IForm
then there's nothing to allow one IForm
to have a firstName
field while another IForm
lacks that field.
type IFormSchema<K extends string> = { name: K }
type IFields<K extends string> = Record<K, string>
class Form<K extends string> {
public fields: IFields<K>;
constructor(schema: readonly IFormSchema<K>[]) {
// initialise this.fields here
throw new Error('Not implemented');
}
}
Example usage:
// no type annotation, and must use `as const` to infer string literal types
const schema = [{name: 'firstName'}, {name: 'lastName'}] as const;
// no type annotation; allow the type argument K to be inferred
const form = new Form(schema);
let ok1: string = form.fields.firstName;
let ok2: string = form.fields.lastName;
let error: string = form.fields.address; // property 'address' does not exist
Upvotes: 3
Reputation: 410
It Isn't Possible. Unless you define the fields in a separate Record Type to define the [key: string]
as an Enum, or in a RecordsType.
enum FieldsEnum = {
FIRST_NAME = 'firstName',
LAST_NAME = 'lastName',
};
type BaseFieldsType = {
// other fields here
// and then the below
[key in FieldsEnum]?: IField;
};
interface IFields extends BaseFieldsType {};
Upvotes: 0
Reputation: 926
You just need to specify it in the keys for IFields:
interface IFields {
firstName: IField;
}
In fact, if you are sure of what keys will be available inside of IFields, you can get rid of the index signature and just use keys.
Upvotes: -1