Reputation: 13
I have a union type which is a result of a template literal type containing two other string union types. It contains all possible permutations of the values as expected. The template literal type is constructed based on generic types on a function, which takes a tuple. Is it possible to narrow the union based on the values passed to that function, so that only the combinations determined by the tuple are present? Hard to explain, so here's a simple code snippet:
function get<T extends string, K extends string>(vals: [T, K][]) {
type FullKey = `${T}${K}`
const answer: FullKey[] = []
for(const v of vals) {
const fullKey: FullKey = `${v[0]}${v[1]}`
answer.push(fullKey)
}
return answer
}
const answer = get([['1', 'a'], ['2', 'b']])
// ^?
// How to make this ('1a' | '2b')[] instead?
I looked at Distributive Conditional Types, that seem like maybe could be the solution here, but I don't know how to apply them here.
Upvotes: 1
Views: 190
Reputation: 26344
Instead of trying to mingle our complex types with the implementation, let's move all of that outside into the return type:
// actual type magic happens outside of the implementation
function get<A extends [T, K][], T extends string, K extends string>(vals: [...A]): { [K in keyof A]: `${A[K][0]}${A[K][1]}` }[number][];
function get(vals: string[][]) {
const answer: string[] = [] // now just string[]
for(const v of vals) {
const fullKey = `${v[0]}${v[1]}` // who CARES that this is just a plain string
answer.push(fullKey)
}
return answer // string[]
}
We use another type parameter A
, and infer vals
as a tuple. Then in the return type we can "map" over the tuple to create the desired result.
Upvotes: 1