#Why does this template literal type need to add `& string` to `keyof Type` to compile?
37 messages · Page 1 of 1 (latest)
Preview:```ts
type PropEventSource<Type> = {
on(eventName: ${string & keyof Type}Changed, callback: (newValue: any) => void): void; // !!! Why can't this just be ${keyof Type}Changed ?
};
/// Create a "watched object" with an 'on' method
/// so that you can watch for changes to properties.
...```
Because "symbols" can be keys inside an object and they don't have a string representation.
oh, let me look that up
Doing & string will discard everything that isn't a string because intersecting incompatible types "returns" never
oh so it is throwing away symbols instead of saying symbols are strings?
If you have:
type X = symbol | true | 10 | 'hello'; and then you do type Y = X & string; then the resulting type will be evaluated to something like: symbol & string | true & string | 10 & string | 'hello' & string which then becomes never | never | never | 'hello which is finally simplified to 'hello' because nevers are discarded in a union.
nope
The "keys", not the values
const someSymbol = Symbol(); const unwatchedPerson = { firstName: 'Foo', [someSymbol]: 10 }
You're creating a "string" key called "sym" at line 15
You need the [], like [sym]: '5'
Wow, if you hadn't spoonfed me this information, I would just end up asking why I can't person.on("symChanged"...
Is there any way I could have figured out my original question without someone telling me? I can't figure out a way to see what keyof Type might be, so I felt like I needed to ask this question if I didn't already know.
You would probably have Googled a little bit and found out that you can use "Extract" instead of the intersection
Actually... Maybe I'm just wrong about an assumption: are object keys any?
Object keys are string | number | symbol
There's an built-in type called PropertyKey that is a union of those
This might sound like whining but I'm trying to teach myself to fish: How could I have figured that out in the playground without you telling me?
okay that's my first time hearing/remembering that. Good to know
Intersections, beside for the simplest case of "merging object types"... could be considered advanced TS
The other way to do it: ```typescript
type PropEventSource<Type> = {
on(eventName: ${Extract<keyof Type, string>}Changed, callback: (newValue: any) => void): void;
};
That makes sense to me, but I still think I would've had to gone through this whole conversation to figure out why I need to do it
Another way would be to do the opposite (remove symbols) ```typescript
type PropEventSource<Type> = {
on(eventName: ${Exclude<keyof Type, symbol>}Changed, callback: (newValue: any) => void): void;
};
I personally learned most of the advanced TS from reading types from the experts on the #ts-discussion channels and some blogs
But like, if I don't know about these two things, is there some way I can figure that out in playground/vs code? Like is there something I could hover over that'll give me a useful popup saying string | number | symbol?
It looks like a lot of TS tutorials assume some level of JS proficiency... and it's known that object keys in JS can only be of those type. I cannot really answer your question properly.
Okay, thanks for the help! I'm wondering if this might be a VS code feature request; I think I should be able to hover over keyof in keyof Type and see a popup that says either string | number | symbol or PropertyKey. Am I missing something?
Maybe I should ask in #ts-discussion ?
Yeah, sure. I would just like to add that only string | number | bigint | boolean | null | undefined can be used in string interpolation ${X}. For example, in a case when you would have type SomeUnion = 10 | 'hello' | { foo: 3 } then it would also not work because an object cannot be used in this context.
(Objects cannot be object keys so a union like that will never happen from using keyof... )
That makes sense 🙂 Thanks