#.
1 messages ยท Page 1 of 1 (latest)
can you share enough of your code to demonstrate the problem? it all comes down to details you haven't provided (like what cart is). for example i stubbed out the missing stuff here and it works the way you hoped:
Preview:```ts
// i made these up:
type ProductCode = 'a' | 'b'
const cart = {
get: () => ({ a: 42 })
}
// ---
export type CartValue = {
[key in ProductCode]?: number;
};
function forProductInCart<T extends CartValue>(
cart: T,
callback: (key: keyof T, value: T[keyof T]) => void
...```
what's the return type of cart.get in your code?
well there's your problem. CartValue is as general as it gets. keyof CartValue is always going to be the entirety of ProductValue
you need to be operating with a more specific subtype of CartValue
like in my example how the return type is inferred as { a: number }
this would really be easier to talk about if you can share your complete code
(or at least a made-up approximation that has enough detail to capture the important stuff)
i suspect that's not quite what you have, because there's a type error before we even get to the type of value (Property 'get' does not exist on type 'CartValue'):
Preview:```ts
type ProductCode = "a" | "b"
export type CartValue = {
[key in ProductCode]?: number
}
// could be any valid CartValue, not literally this
const cart: CartValue = {
a: 1,
b: 2,
}
function forProductInCart<T extends CartV
...```
thanks
so the first thing to be aware of is that what you're doing is inherently unsafe. it's why you had to use a type assertion on keys
!:unsafe-keys
Since TS allows objects to have extra properties not specified in the type, it doesn't assume that all the keys on the type are the only keys on the object. This means that Object.keys returns string[] not a specific type, and for(const key in obj), key is string, (not keyof typeof obj).
If you wish to assume otherwise, this utility is often helpful:
// A signature for `Object.keys` that assumes the only keys are the ones indicated by the type
const unsafeKeys = Object.keys as <T>(obj: T) => Array<keyof T>;
but if you understand that and are okay with this kind of unsafety, i think it'd be possible to subtract out optional keys of T in the signature of forProductInCart. but i don't think that's what you want either, because if cart is really typed as exactly CartValue then that is always just going to be never (because CartValue says that every property is optional)
so tl;dr at the end of the day cart needs to have a more specific type like i said earlier
i might be missing something though. your question doesn't quite make sense to me on a conceptual level. how would the compiler ever be able to know exactly what keys are present at runtime in the case where you have a value that is an arbitrary CartValue? if that's not what you're asking maybe you can rephrase the question
the "values actually present" part is i think where our confusion lies, but what do you think of a dumber setup like this?
Preview:```ts
type ProductCode = "a" | "b"
export type CartValue = {
[key in ProductCode]?: number
}
declare const cart: CartValue
function forProductInCart(
cart: CartValue,
callback: (
key: keyof CartValue,
value: number
) => void
): void {
const keys = Object.keys(cart) as Array<
...```
note that that's still unsafe for the same reason i mentioned above
here's a safe version in case you care about that โ๏ธ:
Preview:```ts
const allPossibleCartKeys = ["a", "b"] as const
type ProductCode = typeof allPossibleCartKeys[number]
// ^?
export type CartValue = {
[key in ProductCode]?: number
}
declare const cart: CartValue
function forProductInCart(
cart: CartValue,
callback: (
key: keyof CartValue,
v
...```
you're welcome ๐