This issue has been identified and reported on the Microsoft/TypeScript GitHub repository under the reference number microsoft/TypeScript#38663. It occurs due to computed properties where the key is a union of string literals, causing them to be widened to a `string` index signature. While this behavior may not be technically incorrect, it lacks specificity to be useful. Some consider it a bug, as referenced in microsoft/TypeScript#13948, while others see it as a design limitation, discussed in microsoft/TypeScript#21030. Regardless, this is the current behavior within the language.
Once the computed property is broadened to an index signature, detecting the bug becomes challenging. Index signatures are not deemed excess properties; hence, a type like `{a: number, b: number} & {[k: string]: boolean}` (which closely resembles the situation) can be assigned to `{a: number, b: number}` without raising an error. However, in reality, the assignment equates to assigning a `boolean` value to either the `a` or `b` property. The compiler overlooks this due to the widening of the index signature.
To address this issue besides being cautious, you can manually create a function that generates the expected type when dealing with computed properties of union key types:
function computedProp<K extends PropertyKey, V>(
key: K, val: V
): K extends any ? { [P in K]: V } : never {
return { [key]: val } as any;
}
The function returns a distributive conditional type resulting in a union of object types. For instance:
const example = computedProp(Math.random() < 0.5 ? "a" : "b", true);
// const example: { a: boolean; } | { b: boolean; }
The union `{a: boolean} | {b: boolean}` accurately represents the intended type of `{[k]: true}`. By using this approach, you'll receive the anticipated error message:
function test1(k: keyof Foo) {
const f: Foo = { ...INIT_FOO, ...computedProp(k, true) }; // error!
// ~
// '{ a: boolean; b: number; } | { b: boolean; a: number; }' is not assignable to 'Foo'.
return f
}
While utilizing a function instead of directly applying a computed property may not be ideal, it allows for maintaining a degree of type safety if required.
Playground link to code