There is a persistent issue known as a bug that has been around for quite some time, which you can find more information about in microsoft/TypeScript#13948. The problem lies in the fact that the computed property key type is incorrectly widened to string
. It is uncertain whether this will be resolved soon or at all. As a temporary solution, it is recommended to use a type assertion to inform the compiler that you are aware of what you are doing:
function Test<N extends string, M extends {}>(name: N, model: M) {
const record = { [name]: model } as Record<N, M>;
return record; // to show something soon
}
const works = Test("okay", "hmm");
// const works: {okay: string}
It should be noted that technically, this approach may not be entirely safe when name
is not solely a single string literal type. For instance, if it is a union of string literals, there is a risk of having more keys than expected at runtime:
const coin = Math.random() < 0.5 ? "heads" : "tails";
const oops = Test(coin, "hmm");
console.log(oops.heads.toUpperCase() + oops.tails.toUpperCase()); // no compile error
// TypeError at runtime!
If you do not intend to pass unconventional input for name
, then this should work fine. However, if you anticipate such scenarios, you might need to take extra precautions to ensure the accuracy of the type of record
:
function TestSafer<N extends string, M extends {}>(name: N, model: M) {
const record = { [name]: model } as
string extends N ? { [k: string]: M | undefined } :
N extends any ? { [K in N]: M } : never;
return record;
}
This implementation behaves as follows:
const fine = TestSafer("okay", "hmm");
// const fine: {okay: string}
const better = TestSafer(coin, "hmm");
// const better: {heads: string} | {tails: string}
const alsoBetter = TestSafer(coin + "!!", "hmm")
// const alsoBetter: { [k: string]: string | undefined; }
This represents the most cautious approach I can think of for users of Test
, although the behind-the-scenes implementation involves a complex mix of assertions and conditional types.
Access code here