Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is setting a dynamic key unsound in typescript

Context / Reproduction

type Example = {
    a: string,
    b: number
}


let value: Example = {
    a: 'hello',
    b: 10,
}

// This IIFE is because typescript will narrow the type if assigned directly like
// const k: keyof Example = 'a'
const k = ((): keyof Example => 'a')()

value = {
    ...value,
    // Obviously this is unsound
    [k]: 5,
}

This passes even in strict mode. Playground link.

Question

Why is typescript unsound here? I want to understand the overarching unsoundness so I can avoid using patterns like this that will not be typesafe. I would also appreciate any relevant documentation or GitHub issues.

like image 907
Zachiah Avatar asked Sep 02 '25 14:09

Zachiah


1 Answers

This is a known issue with TypeScript, documented at microsoft/TypeScript#38663. If you have a computed property name whose type is a union, then TypeScript will widen that type all the way to string (see microsoft/TypeScript#13948), and then more or less just ignores the property entirely. That is, it's even worse than possibly assigning a number to a, it could assign anything to a, including false (see this playground link).

It's not currently classified as a bug, probably because such unsoundnesses with property writes permeate the language:

// widen the type of value, this is considered "safe"
const myVal: { [k: string]: string | number } = value;

// but oops:
myVal.b = "oopsieDoodle";

Fixing such unsoundness would make things safer, but arguably a lot more annoying to use, as evidenced by the many complaints about microsoft/TypeScript#30769. TypeScript is unsound, largely in places where the TS team thinks the cure is worse than the disease. See TypeScript Language Design Non-Goal #3 and TS team comments in microsoft/TypeScript#9825.

like image 100
jcalz Avatar answered Sep 05 '25 15:09

jcalz