TypeScript Version: 3.6.3 and 3.6.4
Search Terms:
Code
I have updated typescript to v3.6.4 from v3.4.5.
In a single typed object, the property can have the different type.
The problem is caused when i need to set the value of specific property of the object dynamically. The type object is reduced never type.
I solved the this issue by wrapping as assertion. However, I think that typescript not throw the error. The example code is as below.
interface RowAttributes {
rowNum: boolean;
checked: boolean;
disabled: boolean;
height?: number;
expanded?: boolean;
}
type RowAttributeValue = RowAttributes[keyof RowAttributes];
const attr: RowAttributes = { rowNum: true, disabled: true, checked: true };
function setRowAttribute(
attrName: keyof RowAttributes,
value: RowAttributeValue
) {
attr[attrName] = value;
}
Expected behavior:
TypeScript not to show the error about the typings.
Actual behavior:
The compile-time error is thrown.

Playground Link:
https://www.typescriptlang.org/play/?target=99&ssl=18&ssc=2&pln=1&pc=1#code/JYOwLgpgTgZghgYwgAgEoHsDuBBMYrABGArpAM7IDeAUMslFgHLEC2AXMoeugDYRwgA3LWQIAFhAQBrCABMOXXvyEjZwMnEJ95nbnwHC6E4AHMxYAPwcQrQtEPIIADwAOA2XKu6lB6gF9qajAATxcUDBw8AhJIADU4HmIUAF40LFx8IlIIMgBtGWD0GDTIzJicgF1hagR0EDIwZDgojgiM6OyKVMp6JlYOfCSAGmQ1DS05Aahh0QlpSeRBlD9qmGIQBDBgOuQyCDA2qKzIAAoRZvxGOBYIDgKikvbjnKGRADcEpNb0o-L4xIg1AAlFRzlFchcoFcbhVkKkPgDhH4gA
Related Issues:
This was updated in 3.5 by #30769, and working as intended.
To be clear on why this is an error, the type system is only looking at the types. If all you know is that you have a keyof T but not which keyof T, and you want to write a value at T[keyof T], then you need an intersection of the selected property types to guarantee the access is sound. In this case you get never because number & boolean & undefined is an empty intersection--there's no value you can provide that's guaranteed to be safe.
While many of the errors introduced by #30769 are annoying, this one is actually good: there's no guarantee at the type level that value is a compatible value for whatever property attrName happens to be, and the compiler is alerting you to this fact. For example, setRowAttribute("disabled", -345) is a perfectly legal call of your function but prior to #30769 would silently write the wrong type to attr.disabled!
You can work around the error by making the function generic, which also has the bonus of better enforcing you get a compatible value:
function setRowAttribute<K extends keyof RowAttributes>(
attrName: K,
value: RowAttributes[K]
) {
attr[attrName] = value;
}
I'll agree that having an indexed access type reduced to never is confusing, though.
@jack-williams On a side note:
value: RowAttributes[K]
Per our discussion a few months back, this is a case where I'd really like to be able to explicitly ask for the "write type" RowAttributes:=K The generic function as written is not 100% type safe, even though the type system accepts it. For example right now one could still accidentally write the wrong type given a dynamic key:
// randomly select a key just to illustrate the blind spot in the type system
let key: keyof RowAttributes = Math.random() > 0.5 ? "disabled" : "height";
setRowAttribute(key, 150);
@fatcerberus I have a PR that implements that internally to fix a bug, but I haven't done the work to surface the type constructor as a new operator. I'm considering putting a proposal together for write indexed access type.
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
Most helpful comment
@fatcerberus I have a PR that implements that internally to fix a bug, but I haven't done the work to surface the type constructor as a new operator. I'm considering putting a proposal together for write indexed access type.