在TypeScript和/或JSDoc中,如何指示记录类型中的某些属性名称是同一类型中同级属性的别名?

在TypeScript和/或JSDoc中,如何指示记录类型中的某些属性名称是同一类型中同级属性的别名?,typescript,visual-studio-code,typescript-typings,jsdoc,Typescript,Visual Studio Code,Typescript Typings,Jsdoc,在TS中对对象属性包类型(又名“记录”)建模的正确方法是什么,其中一些属性名称是其他属性的备用名称(又名“别名”)?特别是,我们希望让VSCode知道别名,这样它将只建议(在IntelliSense自动完成列表中)主属性名,而不是别名,但如果用户手动键入别名,我们也不希望TS编译器失败 下面是更多的背景:我们正在为JS库开发a.d.ts,其中包括接受时间的函数,比如{hour:12,minute:30,second:0}。该库还接受这些文本属性的复数变体,例如{hours:12,minutes:

在TS中对对象属性包类型(又名“记录”)建模的正确方法是什么,其中一些属性名称是其他属性的备用名称(又名“别名”)?特别是,我们希望让VSCode知道别名,这样它将只建议(在IntelliSense自动完成列表中)主属性名,而不是别名,但如果用户手动键入别名,我们也不希望TS编译器失败

下面是更多的背景:我们正在为JS库开发a.d.ts,其中包括接受时间的函数,比如
{hour:12,minute:30,second:0}
。该库还接受这些文本属性的复数变体,例如
{hours:12,minutes:30,seconds:0}
。使用复数变体不是一种最佳实践(除了快速说明它们是允许的之外,可能不会被记录在案)。但是使用这些复数字符串也不会崩溃

如果使用两个变量指定相同的单元,则会发生崩溃:例如
{hour:12,hours:12}

我们应该如何在TS中对这种类型进行建模

我们的目标:

  • 隐藏IDE自动完成中的“错误”变体。我假设JSDoc标记在这方面很有用,但是它没有在TS文档中列出,而且在VSCode中似乎没有任何作用
  • 允许没有TS编译器错误的“错误”变体
  • 如果同一对象文字中包含相同属性的单数和复数变体,则生成TS编译器错误
  • 理想情况下,如果存在较小的单元而没有相应的较大单元,也会产生编译器错误。例如,
    {hour:10,second:30}
    应该产生一个错误。也就是说,我不确定这在TS中是否可行,因为联合类型是如何工作的
  • 理想情况下,即使属性的别名不存在(例如,
    {hours:10,minute:5}
    ),混合使用单数和复数变体也会产生编译器错误。如果TS没有标记此错误也可以,因为这是一个比上面提到的“同一单元的2个变体”案例更罕见的错误
  • 理想情况下(这是可选的,因为我怀疑这可能是不可能的),复数变体将显示信息级别的IDE警告(VSCode中的浅蓝色曲线),以鼓励用户选择正确的变体而不是错误的变体,但不会破坏编译
在TypeScript和/或JSDoc中对这些文本建模的推荐方法是什么,以实现这种期望的行为,或者至少是其中的一个子集

这是包含两种变体的普通类型。此声明不显示上述任何期望的行为

类型时间={
小时?:数字;
分钟?:数字;
第二:数字;
小时数:数字;
分钟数:数字;
秒:数字;
};
JSDoc的标记似乎与我们的意图最为接近,但它似乎在VSCode中没有任何作用,并且它也不能免除复数变体的autocomplete。我还不确定
@alias
是否用于处理别名指向兄弟成员的情况。上面链接的JSDoc手册中的所有示例都是指使用一个名称定义成员,但在运行时使用另一个名称的情况,例如使用类名前缀的静态类函数。我没有看到任何使用
@alias
镜像另一个现有同级属性的示例

我们可以使用JSDoc,但这意味着这些值过去是正常的,但现在不是,这并不完全正确。但它确实提供了一条消息,提醒用户使用另一种变体。这似乎很有用

JSDoc的标记似乎也有帮助,但它似乎在VSCode中也没有任何作用,它也不能免除复数变体的自动完成

  • 隐藏IDE自动完成中的“错误”变体
  • 允许没有TS编译器错误的“错误”变体
我能想到的最接近这个的东西是。这并不会对自动完成隐藏它们,但会用一条线标记它们,以阻止它们的使用

我认为不可能对自动完成隐藏任何根据类型系统也是有效属性的内容。你能得到的最好的东西就是给不鼓励使用的东西做标记

是的,这不是
@deprecated
的完美用法,但是如果您想要IDE级别的提示,这是最好的

type Time = {
  hour?: number;
  minute?: number;
  second?: number;
  
  /** @deprecated */
  hours?: number;

  /** @deprecated */
  minutes?: number;

  /** @deprecated */
  seconds?: number;
};


对于其他人来说,听起来你并不想要一个单一的界面。相反,您需要一个可能的组合的联合。我认为唯一合理的方法是为每个允许的组合创建一个类型

但是您还希望禁止某些组合中的某些属性,您可以使用类似于
{foo?:undefined}
的方法来执行这些操作

所以你可以把一些东西放在一起,比如:

// Singular Components
type Hour = { hour: number }
type Minute = { minute: number }
type Second = { second: number }

type NoHour = { hour?: undefined }
type NoMinute = { minute?: undefined }
type NoSecond = { second?: undefined }
type NoSingular = NoHour & NoMinute & NoSecond

// Plural Components
type Hours = { hours: number }
type Minutes = { minutes: number }
type Seconds = { seconds: number }

type NoHours = { hours?: undefined }
type NoMinutes = { minutes?: undefined }
type NoSeconds = { seconds?: undefined }
type NoPlural = NoHours & NoMinutes & NoSeconds

// Singular time type
type SingularTime = NoPlural & (
  | (Hour & NoMinute & NoSecond)
  | (Hour & Minute & NoSecond)
  | (Hour & Minute & Second)
)

// Plural time type
type PluralTime = NoSingular & (
  | (Hours & NoMinutes & NoSeconds)
  | (Hours & Minutes & NoSeconds)
  | (Hours & Minutes & Seconds)
)

// Final time type
type Time = SingularTime | PluralTime
(为了简洁起见,我省略了
/**@deprecated*/
,但您必须在出现复数属性的所有时间都添加该属性)

可以这样使用:

// Good
const singular: Time = { hour: 1, minute: 2, second: 3 }
const plural: Time = { hours: 1, minutes: 2, seconds: 3 }

// Errors:
const samePropMix: Time = { hour: 1, hours: 2, minute: 3, second: 4 }
const diffPropMix: Time = { hour: 1, minutes: 2, second: 3 }
const missingMinutes: Time = { hour: 1, second: 3 }