typescript - property is missing in type ... but property exists

in js, you can do the following:
let x = 'b';

let y = {
[x]: 6
};

console.log(y);
let x = 'b';

let y = {
[x]: 6
};

console.log(y);
and you will see it has the property b set to 6, as expected. however, typescript trips up in the following code:
let x: string = 'b';

let y: {b: number} = {
[x]: 6,
};

console.log(y);
let x: string = 'b';

let y: {b: number} = {
[x]: 6,
};

console.log(y);
https://www.typescriptlang.org/play/?#code/DYUwLgBAHgXBDOYBOBLAdgcwgXggcgCM8BuAKFNEgE84BvAuNAVwFsCQkBfHCW0iARADaUALpwAbABpSnMqQDGAezTwloAHTAlGABRUAlGSA the code runs properly, but typescript is not happy about it, despite the priperty being right there! it gives me this:
Property 'b' is missing in type '{ [x]: number; }' but required in type '{ b: number; }'.
it doesn't make any sense at all, as [x] and b are the same property! exactly the same thing! so, how do i make typescript see and accept that there is nothing wrong with this, besides @ts-ignore?
26 Replies
Jochem
Jochemโ€ข2d ago
so uh... this is incredibly dumb, but:
let x: 'b' = 'b';

let y: {b: number} = {
[x]: 6,
};

console.log(y);
let x: 'b' = 'b';

let y: {b: number} = {
[x]: 6,
};

console.log(y);
otherwise the compiler won't be smart enough to figure out that x is b, apparently though it technically could
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
that makes sense, use a narrow type ๐Ÿค” i guess it trips because x could be any string i do have a more complex example where this still dies :/ let me try to reproduce it
Jochem
Jochemโ€ข2d ago
I'm guessing it won't run any JS, even if in this minimal example. Like, they could have it do simple assignment stuff like this, but the added benefit would be entirely lost because in this example you could just write b: 6 and be done
Jochem
Jochemโ€ข2d ago
Ha!
type ABC = 'a'|'b'|'c';

class XYZ {
static get GET_A(): ABC & 'a' { return 'a'; }
static get GET_B(): ABC & 'b' { return 'b'; }
static get GET_C(): ABC & 'c' { return 'c'; }

static X: {[key in ABC]: number} = {
[XYZ.GET_A]: 5,
[XYZ.GET_B]: 6,
[XYZ.GET_C]: 7,
};
}
type ABC = 'a'|'b'|'c';

class XYZ {
static get GET_A(): ABC & 'a' { return 'a'; }
static get GET_B(): ABC & 'b' { return 'b'; }
static get GET_C(): ABC & 'c' { return 'c'; }

static X: {[key in ABC]: number} = {
[XYZ.GET_A]: 5,
[XYZ.GET_B]: 6,
[XYZ.GET_C]: 7,
};
}
union types to the rescue or intersection types rather I'm not sure if I fixed it by breaking your example in a useless way, but this does compile with 0 errors
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
let me try ๐Ÿ˜ฎ it works! but it is the same as not having ABC there
Jochem
Jochemโ€ข2d ago
it is and isn't. The compiler treats it as if it's not there, but the person reading it will have more context
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
yup, which is nice
Chooโ™š๐•‚๐•š๐•Ÿ๐•˜
let x: string = 'b';

let y: {[x]: number} = {
[x]: 6,
};

console.log(y);
let x: string = 'b';

let y: {[x]: number} = {
[x]: 6,
};

console.log(y);
The problem with your original TS code is x can be reassigned to another string so using "b" as a literal string value for the key is problematic. Jochem's solution gets around this because x is only allowed to have a value of "b" and no other string value.
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
i figured that but on the 2nd example, it makes absolutely no sebse sense
Chooโ™š๐•‚๐•š๐•Ÿ๐•˜
Typescript primarily does static analysis and not runtime analysis. There is some limited runtime-like analysis when using if statements for type narrowing. The issue with [x] is it requires runtime analysis to determine that x was never reassigned. This code produces an error for the same reason:
type five = 5;
const n:five = 2 + 3;
type five = 5;
const n:five = 2 + 3;
The value of 2 + 3 requires runtime analysis.
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
that makes no sense that's a constant expression static analysis tools can handle that easily
Chooโ™š๐•‚๐•š๐•Ÿ๐•˜
That depends on the compiler/interpreter. A C++ compiler would convert 2+3 to 5 as part of its optimization, and the compiled code would not even contain an addition operation, but TS doesn't do that.
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
other tools automatically detect that 2+3 is 5
Jochem
Jochemโ€ข2d ago
TS isn't a real compiler though it leaves the optimizations to JS itself
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
i know im not talking about that
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
im talking about this
No description
Jochem
Jochemโ€ข2d ago
me too? TS isn't running the code, it's just seeing that Number+Number produces Number, and Number isn't 5
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
but you're talking about the execution, im walking about typescript making sense from it, right? i know, but it is a constant expression
Jochem
Jochemโ€ข2d ago
yes, which isn't getting optimized by typescript
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
it could be a tiny bit smarter and see that constant + constant = constant and automatically create the type 5
Jochem
Jochemโ€ข2d ago
that's effort vs reward stuff. It's going to be pretty uncommon that you'd have "2 + 3" somewhere in your code, and if you do you should just optimize it yourself like, why are you even doing that math every runtime? It makes no sense
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
that's true, but from a typechecking point, i do believe it should be smarter
Jochem
Jochemโ€ข2d ago
it sounds like a really niche thing that would add a potentially huge step to the compilation process that isn't usually going to be sensical in real typescript cause 99.9% of the variables you use aren't constant expressions
Chooโ™š๐•‚๐•š๐•Ÿ๐•˜
Being smarter makes it slower. TSC runs in Javascript, which is a slow language, so it's necessary to not make it very smart. The new Golang version of TSC is still experimental.
แผ”ฯฯ‰ฯ‚
แผ”ฯฯ‰ฯ‚OPโ€ข2d ago
dumb, stupid and fast makes sense to me that's true

Did you find this page helpful?