Playing around with trying to create a function that takes an a single argument object that must not have some set of keys, without making people specify types manually. I was trying to leverage never somehow, but I'm getting caught up in trying to understand how inference works for function arguments that take in a type with a generic parameter.
interface ReservedAttributes {
someKey: string
}
// evaluates to never if the type has a key that intersects
// with the keys in ReservedAttributes
type ValidAttributes<T> = keyof T extends Exclude<keyof T, keyof ReservedAttributes> ? T : never
// This is correctly a never, but it doesn't address this problem
type Test = ValidAttributes<{someKey : string}>
// This doesn't work because the function argument ends
// up being inferred as { [name: string] : string}
function foo1<K extends { [name: string] : string}>(attributes: ValidAttributes<K>) {
// ...
}
foo1({a: 'hi', someKey: ''})
// Roughly equivalent to the above
function foo2< K extends { [name: string] : string}, V extends ValidAttributes<K> >(attributes: V) {
// ...
}
// This one allows V to correctly evaluate to never,
// but I'm not sure how to leverage that
function foo3< K extends { [name: string] : string}, V extends ValidAttributes<K> >(attributes: K) {
// ...
}
foo3({a: 'hi', someKey: ''})
How would you solve this problem?
I think I'd first try staying away from generics and conditional types and do something like this:
type ValidAttributes = Record<string, string> &
Partial<Record<keyof ReservedAttributes, never>>;
declare function foo(attributes: ValidAttributes): void;
foo({ a: 'hi' }); // okay
foo({ a: 'hi', someKey: '' }); // error, someKey
In this case, ValidAttributes is a general string-valued dictionary but with the keys from ReservedAttributes listed as optional properties of type never (optional properties are allowed to be missing, and properties of type never are not really allowed to be present, so an optional property of type never is more or less one that must be missing.) Does that work for you?
If you need to use conditional types in a generic constraint it can be done:
type Attributes<K extends keyof any> = {
[P in K]: P extends keyof ReservedAttributes ? never: string
};
declare function foo<T>(attributes: T & Attributes<keyof T>): void;
foo({ a: 'hi' }); // okay
foo({ a: 'hi', someKey: '' }) // error
But it's more complicated and achieves similar results. Hope that helps; good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With