I'm working in a typescript application that retrieve data from an api (actually OData api).
The api allow to specify which fields to retrieve. Like api/some/getbyid(42)?$select=x,y,z
to get x
, y
and z
fields, along some technical fields that are always retrieved (like id
or author
)
I have model types that reflect the api output :
type APIItem = {
id : number;
author : string;
}
type CustomerModel = APIItem & {
firstName : string;
lastName:string;
age : number
}
I wrap the retrieval logic into an function that ask the ID and the fields to retrieve, call the API and cast the result:
const fakeApi = (id: number, fields : string[]): Promise<APIItem> => {
// build api URL, fetch, ....
const result = JSON.parse(`{
"id": ${id}, "firstName": "Mark", "lastName": "Hamill", "age": 20, "author": "d@some.com"
}`) ;
return Promise.resolve(result);
}
const loadFromDB = async <
TModel extends APIItem
>(
id: number, // Item ID in the api
fields : (keyof Omit<TModel, 'id'> & string)[] // Fields to retrieve. Omit Id because it will be always included
): Promise<TModel> => {
const fromDb = await fakeApi(id, fields);
const result = fromDb as TModel;
return Promise.resolve(result);
}
const customerPromise = loadFromDB<CustomerModel>(42, ['firstName']); // Coding error : missing fields
customerPromise.then(console.log).catch(console.error);
This is working as expected except for one point: the consumer code has to specify all fields to retrieve. In the above example, only the firstName
fields is retrieved, resulting in a incomplete object.
Is there any (simple) way to ensure all fields from the model are provided in the API call ?
As far as I know, there's no way in TS to iterate keys in type, because types are not part of the actual JS output.
What I'd like is to ensure the calling code specify ALL fields (or the function be able to guess itself the fields):
loadFromDB<CustomerModel>(42, ['firstName', 'lastName', 'age']);
This way, the models will always be complete.