【问题标题】:Type definition of object literal constant with complex property types具有复杂属性类型的对象字面量常量的类型定义
【发布时间】:2021-12-08 16:59:25
【问题描述】:

我想要一个 TypeScript 对象字面量常量,其中包含已知但很长的属性列表,这些属性都具有相同的类型。下面是类型的定义和只有三个属性的简化示例。

type ContainerSize = {
  height: string;
  width: string;
  threshold?: number;
};

const CONTAINER_SIZES = {
  '/': { height: '65px', width: '220px' },
  '/info': { height: '220px', width: '220px' },
  default: { height: '700px', width: '500px', threshold: 480 },
} as const;

上面定义的问题是属性类型不是ContainerSize。这可以通过使用Record<string, ContainerSize> 作为对象类型来解决:

const CONTAINER_SIZES: Record<string, ContainerSize> = {
  '/': { height: '65px', width: '220px' },
  '/info': { height: '220px', width: '220px' },
  default: { height: '700px', width: '500px', threshold: 480 },
} as const;

但现在任何字符串都是有效的键,因此CONTAINER_SIZES['not-existing'] 不会显示错误。

除了像下面的示例那样两次写入属性之外,还有其他方法来定义对象字面量吗?

const CONTAINER_SIZES: Record<'/' | '/info' | 'default', ContainerSize> = {
  '/': { height: '65px', width: '220px' },
  '/info': { height: '220px', width: '220px' },
  default: { height: '700px', width: '500px', threshold: 480 },
} as const;

【问题讨论】:

  • 为什么你希望属性值为ContainerSize,具体来说?我猜这是为了开发者体验,但有什么具体的吗?
  • 是的,它是为了开发者体验。如果某个函数参数类型是ContainerSize,最好从CONTAINER_SIZES 中立即看到它具有正确的属性类型,而不是检查它们是否匹配。

标签: typescript


【解决方案1】:

两种选择:

  1. 通过先单独定义对象来推断键

  2. 返回ContainerSize的无操作函数

从前一个对象推断键

必须可以在一个步骤中完成此操作,但您可以通过以下两个步骤完成此操作:

const _sizes = {
    '/': { height: '65px', width: '220px' },
    '/info': { height: '220px', width: '220px' },
    default: { height: '700px', width: '500px', threshold: 480 },
} as const;
const CONTAINER_SIZES: Record<keyof typeof _sizes, ContainerSize> = _sizes;

Playground link

正如您所指出的(谢谢!),这会丢失属性值的只读方面(CONTAINER_SIZE 的属性值和ContainerSize 对象的值),我们可以用Readonly 修复它两个地方:

const CONTAINER_SIZES: Readonly<Record<keyof typeof _sizes, Readonly<ContainerSize>>> = _sizes;

Playground link

无所事事的功能

当我希望在提供值(开发人员体验的东西)时强制执行 ContainerSize 时,我使用了另一个选项,即拥有一个无操作功能:

const cs = (size: ContainerSize) => size;

然后:

const CONTAINER_SIZES = {
    '/':      cs({ height: '65px', width: '220px' }),
    '/info':  cs({ height: '220px', width: '220px' }),
    default:  cs({ height: '700px', width: '500px', threshold: 480 }),
};

这为传递到cs 的对象文字中的属性名称等提供了自动完成功能。 (在我做这个的一个项目中,我很快发现我想在函数中添加一些跨属性验证逻辑,所以最终它不是什么都不做......)

Playground link

【讨论】:

  • 如果这是一个常见的定义,你也可以使用函数来进行推理行为
  • @TitianCernicova-Dragomir - :-) 伟大的思想都一样,我记得在发布后就在一个项目中这样做并添加它。指定值时会产生更好的 devex。
  • 当从前一个对象推断键时,当前解决方案允许修改属性(as const 不适用于CONTAINER_SIZES)。使用 Readonly 实用程序类型将解决此差异:const CONTAINER_SIZES: Readonly&lt;Record&lt;keyof typeof _sizes, ContainerSize&gt;&gt; = _sizes;
  • @SamuliAsmala - 感谢您指出这一点!我认为您需要属性类型而不是对象上的Readonlyconst CONTAINER_SIZES: Record&lt;keyof typeof _sizes, Readonly&lt;ContainerSize&gt;&gt; = _sizes;tsplay.dev/w6XnYm
  • 好点!我错过了那个财产分配。实际上我认为我需要 Readonly 属性类型和对象 tsplay.dev/WPxPqW
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-09
  • 1970-01-01
  • 1970-01-01
  • 2017-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多