#How should I best define a generic parameter constraint to limit object values?

11 messages · Page 1 of 1 (latest)

weary nacelle
#

I'm currently using <T extends { [K in keyof T]: AllowableValue }> - but this is getting very verbose as I have it now in many places.

I can't use Record<string, AllowableValue> because then it requires the given type I am providing as a generic argument to have an index signature, where I want to be able to provide a type such as {x: AllowableValue, y: AllowableValue} with no index signature.

urban hedge
#

Why do you need such a constraint in the first place, are you actually going to do something like t[someKey]?

weary nacelle
#

I am trying to restrict arbitrary object signatures because I do operations over values and need them to be of specific types, however, I don't care what the keys are

urban hedge
#

How are you doing the operations?

weary nacelle
#

Object.entries

#

basically I want a generic bound that allows { key: allowableValue, ...}, disallows { key: <anything else> }, and does not require an index signature. Using Record<string, AllowableValue> requires an index signature in provided types, which doesn't work if you have values of a specific interface type without an index signature rather than arbitrary objects.

urban hedge
#

Interfaces are open, they can be declaration merged to add more properties later, eg properties that no longer satisfy your constraint. Adding an index signature would be the correct way, because it will ensure only properties that satisfy your constraint can be added.

fossil wasp
#

it will ensure only properties that satisfy your constraint can be added
it's better than not having the index signature, but since objects can always have excess runtime properties not reflected in their type this pattern can never be truly safe

#

this is true even in simple cases without generics involved. for example:

dawn juniperBOT
#
mkantor#0

Preview:```ts
interface Foo {
[key: string]: number
}

const a = {key: "not a number"}
const b: {} = a
const c: Foo = b

Object.entries(c).map(
([key, value]) => value.toFixed() // boom
)```

fossil wasp
#

a Map<string, AllowableValue> could potentially be safer if it makes sense in context