skip to Main Content

I have a typescript interface:

interface MyInterface {
    property1?: string;
    property2?: string;
};

type InterfaceKey = keyof MyInterface;

The code below creates an object based on MyInterface. There is a function called verifyObjectProperty that allows the user to pass in an InterfaceKey (‘property1’ or ‘property2’) as a second parameter.

The function validates that the object has a string value for the given key, so it can no longer be undefined.

// - Create an object based on the interface
const myObject: MyInterface = {
    property1: 'a string',
}

const verifyObjectProperty = (
    objectToVerify: MyInterface,
    properyToVerify: InterfaceKey
): MyInterface => {
    // - Make sure object has the desired property
    if (objectToVerify[properyToVerify] === undefined) {
        objectToVerify[properyToVerify] = 'a new string';
    }

    // - Return the object
    return myObject;
};

I want to make it so the verifyObjectProperty function returns a typescript interface that shows which string is guaranteed to be there.

const verifiedObject = verifyObjectProperty(myObject, 'property1');
type property1 = typeof verifiedObject['property1']; // string
type property2 = typeof verifiedObject['property2']; // string | undefined

2

Answers


  1. I don’t know where myObject comes from in your example, so I figured it is objectToVerify.

    This seems to work as specified in the TypeScript playground:

    interface MyInterface {
      p1?: string;
      p2?: string;
    };
    
    function ensureProperty<TProp extends keyof MyInterface>(x: MyInterface, p: TProp) {
      if (x[p] === undefined) {
        x[p] = 'a'
      }
      return x as Omit<MyInterface, TProp> & Record<TProp, string>;
    }
    
    var test = ensureProperty({}, 'p2');
    

    It uses Omit<> to remove the ensured property, which is re-added in the cast when returning the object.

    Login or Signup to reply.
  2. Use ts conditional types and mapped types. These features allow you to create new types based on the properties of existing types.

    First, create a helper type that will take a property key and return a new type with that property guaranteed to be a string.

    type EnsureString<T, K extends keyof T> = T & { [P in K]: string };
    

    This type takes two parameters: T is the original type (MyInterface), and K is the key of the property you want to check as a string. It returns a new type that is the same as T, but with the property K guaranteed to be a string.

    Then, modify verifyObjectProperty to use this helper type.

    const verifyObjectProperty = <K extends keyof MyInterface>(
       objectToVerify: MyInterface,
       propertyToVerify: K
    ): EnsureString<MyInterface, K> => {
       // - Make sure object has the desired property
       if (objectToVerify[propertyToVerify] === undefined) {
           objectToVerify[propertyToVerify] = 'a new string';
       }
    
       // - Return the object
       return objectToVerify as EnsureString<MyInterface, K>;
    };
    

    After calling verifyObjectProperty, it will infer that the returned object has a string value for the specified property.

    const verifiedObject = verifyObjectProperty(myObject, 'property1');
    type property1 = typeof verifiedObject['property1']; // string
    type property2 = typeof verifiedObject['property2']; // string | undefined
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search