When I try to assign a value to a Record
using a computed property name, I get a type error:
function Test<N extends string, M extends {}>(name: N, model: M) {
const record: Record<N, M> = { [name]: model };
}
Type '{ [x: string]: M; }' is not assignable to type 'Record<N, M>'.ts(2322)
Why does the rvalue have the type { [x: string]: M; }
and not { [x: N]: M }
?
This is a longstanding known bug, see microsoft/TypeScript#13948, where the computed property key type is widened to string
erroneously. Not sure if this will be fixed soon, or ever. For now, I'd say that you should use a type assertion to tell the compiler that you know what you're 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}
Note that it's technically not safe to do this, in the case where name
is not of a single string literal type. For example, if it's a union of string literals, you get something claiming to have more keys than it has 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're not planning to pass in weird things for name
that's fine. Otherwise you could start going crazy to make the type of record
as true as possible:
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;
}
which has the following behavior
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; }
That's about the safest I can imagine making things for users of Test
, while the implementation is a big mess of assertions and conditional types.
Link to code
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With