带有不存在键的打字稿键值对象

Typescript key value object with non-existing keys

我想定义从我的数据库下载的对象类型。

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},
}

下面的代码没问题,保证我的orderData存在

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

这不太好,但是打字稿没有给我任何错误。 someRandomId 可以来自任何地方,例如用户输入的值。

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

我可以将我的类型更改为以下,但我想避免,因为它会弄乱我的 for-in 循环。

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

是否有更优雅的解决方案?

您定义的 ActiveOrders 类型如下:

// An ActiveOrders is a map...
type ActiveOrders = {
    // with arbitrary string keys in it that correspond to map values...
    [orderId: string]: {
        // that must contain a `name` key whose value is a string
        name: string;
        // and a `price` key whose value is a number
        price: number;
    }
};

根据这个定义,activeOrders['someRandomId'] 是有效的。 ActiveOrders 类型允许任何字符串键存在于其映射中,并且该类型不知道这些键是什么。

使用 TypeScript 4.1+,您确实有 过度定义什么构成可接受的密钥,但请记住,虽然您可能正在使用 TypeScript 进行编码,但您的代码正在被转换为 JavaScript您的类型不再受支持。

所以安全检查每个数据真的取决于你;这些类型只是开发人员的守卫rails。如果你想对数据的形状进行某种运行时验证,你可以使用像 yup 这样的模式验证器,但这似乎很奇怪地放在 TypeScript 旁边。

根据定义,TypeScript 会假定您用来访问此类对象的任何键都是有效的,因为您基本上说过它是有效的。

您可以使用 noUncheckedIndexedAccess 强制 typescript 假设在使用任意字符串索引对象时 总是 结果未定义的机会。

您可以使用条件类型来指定对象何时假定始终已定义以及何时可以未定义(或任何实际情况):

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[0]]: {
    name: 'banana', 
    price: 123
  },
  [keys[0]]: {
    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