Home > database >  Get type of object property value by Object property key and Object type
Get type of object property value by Object property key and Object type

Time:01-12

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 !

Playground

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

Code in the TS Playground

  • Related