Reputation: 89
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 => {
const data = res.json() as ApiResponse<ids>;
// fine to access by the id
const works = data[ids[0]];
// undefined, because 5 isn't one of the ids we requested so won't be in the reponse type.
const wontWork = data[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
Upvotes: 0
Views: 57
Reputation: 370689
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.
Upvotes: 2