Assume
abstract class CustomisationProvider {
abstract fetch(id: string): any;
}
I want to add type definitions the fetchThings
function
function fetchThings(sources, ids, services){
for(const s in sources){
services[s].fetch(ids[s]);
}
}
One valid type annotation that is accepted by the typescript compiler is
function fetchThings<
IDs extends { [key: string]: any }
>(
sources: string[],
ids: IDs,
services: {
[key: string]: CustomisationProvider
}
){
for(const s in sources){
services[s].fetch(ids[s]);
}
}
However, this does not capture the fact that the elements in sources
are expected to be key of services
and keys of ids
and that ids[source[i]]
is string.
Starting from this answer I would expect the following to work
type KeysMatching<T, V> = {[K in keyof T]-?: T[K] extends V ? K : never}[keyof T];
function fetchThings<
P extends KeysMatching<IDs, string>,
IDs extends { [key: string]: any }
>(
sources: P[],
ids: IDs,
services: {
[key: P]: CustomisationProvider
}
){
for(const s in sources){
services[s].fetch(ids[s]);
}
}
However, the TS compiler 4.3.5 will reject this with the message An index signature parameter type must be either 'string' or 'number'.
function fetchThings<
P extends keyof IDs,
IDs extends { [key: string]: any }
>(
sources: P[],
ids: IDs,
services: {
[key: P]: CustomisationProvider
}
){
for(const s in sources){
services[s].fetch(ids[s]);
}
}
It will fail, what is very intriguing, since if P
is key of IDs
why it could not be a key of services
. I have read that we cannot mix keys of different types.
i.e.
interface Y {
[key: string ]: any,
[key: number ]: any
}
is valid
but
interface X {
[key: string | number ]: any
}
is not.
I tried to explicitly use type P & string
, or Filter<P, string>
where type Filter<T, U> = T extends U ? T : never;
without success.
How could this type of relation to be represented in the type signatures?
Thank you.