When the getValues()
function is called without explicit type parameters, the Typescript code functions correctly. However, calling it with explicit type parameters can result in errors (as seen in invocation
getValues<'a' | 'b' | 'c' >(store, 'a', 'b')
below).
type Store = Record<string, string>;
function getValues<T extends string>(
store: Store, ...keys: T[]
): {[P in T]: string} {
const values = {} as {[P in T]: string};
keys.forEach((p) => {
const value = store[p];
if (value) {
values[p] = value;
} else {
values[p] = 'no value';
}
});
return values;
}
// Usage / requirements:
const store: Store = {}; // assume it is filled with arbitrary values
// The following line infers values1 to be of type
// {a: string, b: string}, which is the desired behavior
const values1 = getValues(store, 'a', 'b');
// The next line does not compile, as intended
// Argument of type 'b' is not assignable to parameter of type 'a' | 'c'
const values2 = getValues<'a' | 'c'>(store, 'a', 'b');
// In the following line, values3 is inferred to be of type
// {a: string, b: string, c: string}, which is NOT the desired behavior
// It should ideally be forbidden by the compiler, similar to how values2 was handled
const values3 = getValues<'a' | 'b' | 'c' >(store, 'a', 'b');
// Now values3.c can be accessed,
// although values3.c cannot have a value and will raise an error when used
Is there a way to prevent the caller from specifying explicit type parameters or any other method to enhance the type safety of this code without making the return type of the getValues function partial or its return type's properties optional?
I have a few ideas that I am unsure about their feasibility in Typescript:
Idea 1: Restrict providing explicit type parameters
Idea 2: Eliminate value arguments in getValues and access the provided type arguments at runtime
Idea 3: Mandate all provided type arguments to also be used as value arguments. This means the declaration of constant values3 above would trigger a compilation error.
I understand that the issue stems from the risky type assertion as {[P in T]: string}
, so I am seeking ways to ensure type safety.