I’m making an API call that is of the format url/ids=[id1,id2,id3] and returns an object of the form { id1: {...}, id2: {...}, id3: {...} }
I can type this as interface ApiResponse { [key: number]: ResponseShape }, but I actually have more type information than that, as that type loses the relationship between the inputted ids and the response object’s keys.
Is there a way to use the variable ids in the type? I know TS is compile time, but I was hoping I for the following scenario:
function apiCall(ids: number[]) {
const someRandomNumber = 10;
fetch(url).then(
res: ApiResponse<ids> => {
// fine to access by the id
const works = res[ids[0]];
// undefined, because 5 isn't one of the ids we requested so won't be in the reponse type.
cons wontWork = res[someRandomNumber]
}
);
}
I’m guessing this doesn’t exist since it’s pretty niche and right on the border of what should happen at compile time, but I’m hoping there’s a way to do type ApiResponse<T extends number[]> = { [id in T[number]]: ResponseShape } and pass the ids parameter in as a type
>Solution :
If you make apiCall generic and then use T[number] to get a union of all the passed IDs, you can then construct a Record where the keys are those numbers.
type ApiResponse<T extends number> = Record<T, 'foo'>;
function apiCall<T extends readonly number[]>(ids: T) {
return fetch('url')
.then(res => res.json()) as Promise<ApiResponse<T[number]>>;
}
apiCall([1, 2] as const)
.then((result) => {
console.log(result[1]); // Allowed
console.log(result[3]); // Not allowed
});
You’ll have to be sure to pass a tuple (like [1, 2] as const) and not a number[] for the type of the individual IDs to be preserved across the script.