2

I am creating a database schema which requires a string name of each model type and I need to avoid using classes. My database models are defined by an enum and interfaces:

const enum Models {
  Person = 'Person',
  Vehicle = 'Vehicle',
}

export interface Person {
  firstname: string;
  lastname?: string;
}

export interface Vehicle {
  model: string;
  year?: string;
}

The DatabaseModel interface links the modelType name and associated data:

export interface DatabaseModel {
  modelType: any;
  data: any;
}

This type maps the interface to a specific enum. I am not sure how to use this exactly, but I think it is needed for type checking to work correctly.

export type ModelInterfaceMap = {
  Person: Person;
  Vehicle: Vehicle;
}

I have created a function which creates a DatabaseModel as follows:

export function create(modelType: Models, data ): DatabaseModel {
  return {
    modelType,
    data,
  };
}

My question is, how do I add typings to the above code so that I can call the create function and only reference the model type once in the first modelType parameter and still get all the benefits of type checking the data against it's associated model?

This should pass type checking

var newPerson = create(Models.Person, { firstname: 'John', lastname: 'Doe' });
var newVehicle = create(Models.Vehicle, { model: 'Ford', year: '2018' });

This should fail type checking because the Person interface does not have a year

var newPersonFail = create(Models.Person, { firstname: 'John', lastname: 'Doe', year: '2018' });

I tried using generic types like this, but this requires writing the model type twice; once in the generic type and once when passing in the modelType. I would like to avoid having to specify the modelType twice.

export function create<T>(modelType: Models, data : <T> ): DatabaseModel<T> {...}
1
  • Ahh yes, I will edit that. Commented Jan 7, 2019 at 8:16

1 Answer 1

3

You need to capture the actual enum member passed to the function in a generic type parameter (as an enum literal type). Having this type we can use it to index into the ModelInterfaceMap to get the apropriate type for the data based on the passed in enum:

export function create<T extends Models>(modelType: T, data: ModelInterfaceMap[T]): DatabaseModel {
    return {
        modelType,
        data,
    };
}

var newPerson = create(Models.Person, { firstname: 'John', lastname: 'Doe' });
var newVehicle = create(Models.Vehicle, { model: 'Ford', year: '2018' });

var newPersonFail = create(Models.Person, { firstname: 'John', lastname: 'Doe', year: '2018' });
Sign up to request clarification or add additional context in comments.

1 Comment

@Chris I try :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.