【问题标题】:Typescript key value object with non-existing keys具有不存在键的打字稿键值对象
【发布时间】:2022-01-25 14:59:50
【问题描述】:

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

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}

有没有更优雅的解决方案?

【问题讨论】:

  • 您要确保密钥始终以orderId 开头吗?
  • 您需要在 tsconfig 中使用 noUncheckedIndexedAccess 选项,但这会使这两种情况都抛出错误。据我所知,没有办法让第一个工作而第二个失败
  • 验证你的 orderIds 不是 Typescript 的工作——你已经告诉 Typescript 期望对象中有一个字符串 [orderId] 键,这是它唯一知道/关心的事情。然后验证 orderId 是您的工作;如果您担心可能会使用无效的 orderIds 来访问该对象,您应该相应地键入它(就像您使用 undefined 所做的那样)。
  • @RameshReddy 不,我的 orderIds 是随机生成的
  • 是的,当涉及类型联合时,打字稿将尝试确定某物是哪种类型。检查我的答案,了解使用条件类型执行此操作的方法

标签: typescript loops dictionary object undefined


【解决方案1】:

根据定义,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

【讨论】:

  • 感谢您的回答。我不认为我可以使用您的符号作为解决方案,因为 for-inObject.keys 强制我的键是字符串(这可能是 JS 限制)。我仍然会将此作为解决方案,因为您提供了可能的解决方法noUncheckedIndexedAccess
  • Object.getOwnPropertySymbols 可能适用于迭代符号键
【解决方案2】:

您定义的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+,您确实有 some freedom 过度定义什么是可接受的键,但请记住,虽然您可能正在使用 TypeScript 编码,但您的代码正在被转换为不再支持您的类型的 JavaScript。

因此,安全检查每个数据确实取决于您;这些类型只是作为开发人员的护栏。如果您想对数据的形状进行某种运行时验证,可以使用像 yup 这样的模式验证器,但这似乎奇怪地放在 TypeScript 旁边。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-15
    • 2020-04-20
    • 2019-10-03
    • 2019-09-29
    • 2018-09-23
    • 1970-01-01
    • 2020-01-19
    • 1970-01-01
    相关资源
    最近更新 更多