I have the type of an object and a key for objects of that type. How to get the type of the corresponding value to the key?
I have the following function:
function toPartial<T>(key: keyof T, value: T[typeof key]): Partial<T> {
const partial: Partial<T> = {};
partial[key] = value;
return partial;
}
The current type of value
is wrong because it includes the types of all keys with the type of the key.
Example:
type ExampleType = {
id: string,
amount: number
}
const key = "amount";
const value = "abc"
toPartial<ExampleType>(key, value); // with my implementation there is no type error, but it should because a string is not assignable to `amount` of `Example type`.
CodePudding user response:
Something like that works:
function toPartial<T, U extends keyof T>(key: U, value: T[U]): Partial<Pick<T, U>> {
const partial: Partial<T> = {};
partial[key] = value;
return partial;
}
type ExampleType = {
id: string,
amount: number
}
const foo = toPartial<ExampleType, "amount">("amount", 3);
The value is linked to the key. Also the return type might be improved by using Pick
!
CodePudding user response:
This can be accomplished entirely via inference of arguments, but it will require a higher-order helper function in order to create the appropriate constraints.
This is because TypeScript doesn't currently allow for supplying only some generic type parameters: it's either all or none at the moment. You can read more about this at the following GitHub issue: microsoft/TypeScript#10571 - Allow skipping some generics when calling a function with multiple generics
Here's what that helper function would look like:
type PartialFactoryFn<T extends Record<PropertyKey, unknown>> =
<K extends keyof T, V extends T[K]>(key: K, value: V) => Partial<T>;
/** @returns the `toPartial` function that you described in your question */
function createPartialFactory <T extends Record<PropertyKey, unknown>>(): PartialFactoryFn<T> {
return (key, value) => {
const partial: Partial<T> = {};
partial[key] = value;
return partial;
};
}
You can use it with your example type and values this way:
type Example = {
amount: number;
id: string;
};
const partialExampleError = createPartialFactory<Example>()("amount", "abc"); /* Error
~~~~~
Argument of type 'string' is not assignable to parameter of type 'number'.(2345) */
const partialExample = createPartialFactory<Example>()("amount", 10); // ok
partialExample.amount;
//^? (property) amount?: number | undefined
partialExample.id;
//^? (property) id?: string | undefined
And if you want to create a dedicated function that is constrained by a specific type (e.g. your example type), you can also write it this way:
const toPartialExample = createPartialFactory<Example>();
toPartialExample("amount", "abc"); /* Error
~~~~~
Argument of type 'string' is not assignable to parameter of type 'number'.(2345) */
toPartialExample("amount", 10); // ok