Search code examples
typescriptloopsdictionaryobjectundefined

Typescript key value object with non-existing keys


I want to define the object type that I download from my database.

type ActiveOrders = {[orderId: string]: {name: string; price: number}}

const activeOrders: ActiveOrders = {
  'orderId1': {name: 'apple', price: 123},
  'orderId2': {name: 'banana', price: 123},
  'orderId3': {name: 'tesla', price: 99999999},
}

Following code is fine, and my orderData is guaranteed to exist.

for(const orderId in activeOrders) {
  const orderData = activeOrders[orderId]
  // This is fine, orderData is guaranteed to exist
  const {name, price} = orderData
}

This is NOT fine, but typescript is not giving me any error. someRandomId can come from anywhere such as user entered value.

const orderData2 = activeOrders['someRandomId']
// This is NOT fine, orderData2 is possibly undefined, but typescript says it is guaranteed to exist.
const {name, price} = orderData2

I can change my type to following but I want to avoid as it will mess up my for-in loop.

type ActiveOrders = {[orderId: string]: {name: string; price: number} | undefined}

Is there more elegant solution to this?


Solution

  • By definition TypeScript will assume that any key you use to access this type of object is valid because you basically said it would be.

    You can use noUncheckedIndexedAccess to force typescript to assume that there is always a chance the result is undefined when using any arbitrary string to index the object.

    You can use a conditional type to specify when the object is assumed to always be defined and when it could be undefined (or anything really):

    type ActiveOrders = { 
      [orderId in string|symbol]: orderId extends string ? ({
        name: string; 
        price: number
        }|undefined) : {
        name: string; 
        price: number
        }
    }
    
    const keys : symbol[] = [
      Symbol('a'),
      Symbol('b'),
      Symbol('c')
    ];
    
    const activeOrders: ActiveOrders = {
      [keys[0]]: {
        name: 'apple', 
        price: 123
      },
      [keys[1]]: {
        name: 'banana', 
        price: 123
      },
      [keys[2]]: {
        name: 'tesla', 
        price: 99999999
        },
    }
    for(const symbolKey of keys) {
      const orderData = activeOrders[symbolKey]
      // This is fine, indexed with a symbol the data is assumed to be there
      const {name, price} = orderData
    }
    

    Playground link