Open fmauquie opened 3 years ago
As a temporary workaround: Writing [`person-${1}` as const]: { ... }
works.
As a temporary workaround: Writing
[`person-${1}` as const]: { ... }
works.
It does !
I actually create the key from a function that returns the constructed string type, and you can't "as const" a function return value. Forcing the type ("as PersonKey") does not work either.
type PersonKey = `person-${Person["id"]}`;
function personKey(id: number): PersonKey {
return `person-${id}`;
}
I simplified it as much as I could for the report.
The workaround I'm using is to assign the value to an explicitly-typed variable first, then assign this variable to the attribute:
/* When using computed properties as object key, TS does not check value type.
https://github.com/microsoft/TypeScript/issues/45798
DO NOT INLINE THAT ASSIGNMENT
*/
const myPerson: Person = { id: 1, name: "" };
persons = {
...persons,
[personKey(1)]: myPerson /* see comment on myPerson */,
}
The type is still not checked on assignment in persons
, but it is checked just above. It will work until someone (or myself next month) bypasses the warning in the comment and inlines it.
This is a side effect of template literals having type string
by default, not the matching template literal type.
A few possibilities come immediately to mind
as const
here as elsewhere.x[template]
, which is pretty close to equivalent to computed property names.string
by default. I don't remember the cases where that made sense, but they were quite different than these.I think I imagined that template literals as computed property names should be as narrow as possible, but I probably was overconfident about that when labeling it a bug. In hindsight, I think this should be Suggestion / Awaiting More Feedback or In Discussion. Iām pretty sure calling it a bug is too optimistic and calling it a design limitation is too pessimistic. I think we could change the behavior if we were confident it's worth the tradeoffs.
Another option:
const x: `person-${Person["id"]}` = `person-${1}`
I experimented with a couple of implementations. I'll write up my notes on a PR.
I just noticed something similar and am 99% sure it's the same issue, although I think I might have come up with a weird ish edge case? (If this is a separate issue, please let me know and I'll make a new issue).
In the below code/playground link I find it interesting how withExplicitAssign
correctly has an issue with a wrong index signature, and withExplicitSpread
only has an issue with the "static" no.5
, but has no issue with no.${number}
- and has a different error compared to withExplicitAssign
.
The withImplicit
doesn't have an issue with any of them and throws no errors.
const users = ['frank', 'charles', 'bob', 'whitney', 'richard'];
interface UsersParams {
[key: `username.${number}`]: string;
}
const withImplicit = users.reduce<UsersParams>((acc, cur, index) => ({
...acc,
[`username.${index}`]: cur,
[`no.${index}`]: cur,
[`no.5`]: cur
}), {});
const withExplicitAssign = users.reduce<UsersParams>((acc, cur, index) => {
acc[`username.${index}`] = cur;
acc[`no.${index}`] = cur; // Element implicitly has an 'any' type because expression of type '`no.${number}`' can't be used to index type 'UsersParams'.(7053)
acc[`no.5`] = cur; // Element implicitly has an 'any' type because expression of type '"no.5"' can't be used to index type 'UsersParams'. Property 'no.5' does not exist on type 'UsersParams'.(7053)
return acc;
}, {});
const withExplicitSpread = users.reduce<UsersParams>((acc, cur, index) => {
acc = {
...acc,
[`username.${index}`]: cur,
[`no.${index}`]: cur,
[`no.5`]: cur // Type '{ "no.5": string; }' is not assignable to type 'UsersParams'. Object literal may only specify known properties, and '[`no.5`]' does not exist in type 'UsersParams'.(2322)
}
return acc;
}, {});
Bug Report
š Search Terms
assignment check type record template literal index spread
š Version & Regression Information
āÆ Playground Link
Playground link with relevant code
š» Code
š Actual behavior
No error on the assignment on last line.
š Expected behavior
There should be a type error because the object being assigned is not a
Person
.Error triggers correctly in any of the following scenarios:
{ "person-1": {/* error triggers correctly */}
), or a "simple" computed property (e.g.{ ["person-1"]: {/* error triggers correctly */}
)Still, thank you all for the great language !