The first issue is capturing the string literals when you call make
. You could do this in one of several ways. You could use tuples in rest parameters (make<L extends string[]>(...keys: L): Foo<L>
but this will require you call the make
function as Foo.make('a', 'b')
.
Another option is to use the fact that the compiler will preserve literal types if they are assigned to a type parameter constrained to string
. This will preserve the current way of calling.
The issue of getting the union is simple, you can use a type query.
export class Foo<K extends string[]> {
static make<L extends V[], V extends string>(keys: L): Foo<L> {
return new Foo();
}
get(key: K[number]) { // index type
}
}
const x = Foo.make(['a', 'b'])
x.get("a")
x.get("A") // err
Playground Link
Another option is not to use the tuple type in L
, just capture the union directly when calling make
:
export class Foo<K extends string> {
static make<V extends string>(keys: V[]): Foo<V> {
return new Foo();
}
get(key: K) { // index type
}
}
const x = Foo.make(['a', 'b'])
x.get("a")
x.get("A") // err
Playground Link