我试图为我的项目定义类型,以便有一个通用的方法,同时能够缩小工作流中对象的特定属性.

satisfies运算符似乎正是我所需要的,因为我可以在对象上使用泛型类型,并保持一个强的类型安全性,同时避免过多的样板代码来缩小类型.

但我在使用可选属性时遇到了困难,因为它们不会作为选项出现在自动完成中,并且我得到了一个IDE错误.

const formatterFunctions = {
    foo: () => (true),
    bar: undefined,
} as const
type FormatterFunctionsKeys = keyof typeof formatterFunctions

type MyObj = {
    type: 'normal' | 'function',
    message: string,
    formatter?: typeof formatterFunctions[FormatterFunctionsKeys]
};

const objArray = {
    foo: { type: 'function', message: 'someMessage', formatter: formatterFunctions.foo},
    bar: {type: 'normal', message: 'barMessage'}
 } satisfies {[K in FormatterFunctionsKeys]: MyObj}

 for (const [key, entry] of Object.entries(objArray)) {
    entry.formatter // <= error here 'formatter' doesn't appear in the autocomplete, and typing it manually creates an error. The only property are 'type' and 'message'.
 }

TS错误消息:

 Property 'formatter' does not exist on type '{ type: "function"; message: string; formatter: () => boolean; } | { type: "normal"; message: string; }'.

你能帮我找出我定义类型的方式有什么问题吗? 我的目标是让自动补全和正确的打字,同时仍然使用satisfies.

精确度:代码是我的更复杂 case 的一个例子.但MyObj对象可以是类型:‘Normal’,并且具有格式化程序功能.

推荐答案

你不能微调the satisfies operator的工作方式,所以没有直接的方式来说"请也跟踪省略的optional properties".假设我们将相关类型命名为ObjArray:

type ObjArray = { [K in FormatterFunctionsKeys]: MyObj };

然后写下

const objArray = {
  foo: { type: 'function', message: 'someMessage', formatter: formatterFunctions.foo },
  bar: { type: 'normal', message: 'barMessage' }
} satisfies ObjArray;

只判断/保证objArray的类型将是assignable to ObjArray,而不是它可以直接用作一个.在完美的sound类型系统中,这是等价的,但类型系统中有一些漏洞,处理可选属性的方式就是这样一个漏洞(有关基本问题,请参阅this comment on microsoft/TypeScript#42479).

但既然你知道objArray可以被赋值给ObjArray,如果你遇到麻烦,你可以采取的一种方法是将它分配到希望得到ObjArray的某个地方,然后使用它.

您可以通过将变量实际赋值给另一个变量来完成此操作:

const myObjArray: ObjArray = objArray; // okay
for (const [key, entry] of Object.entries(myObjArray)) {
  entry.formatter // okay
}

或者创建一个函数并将objArray传递给该函数:

function doStuff(objArray: ObjArray) {
  for (const [key, entry] of Object.entries(myObjArray)) {
    entry.formatter // okay
  }
}
doStuff(objArray); // okay

或者只是为了权宜之计而使用type assertion.

for (const [key, entry] of Object.entries(objArray as ObjArray)) {
  entry.formatter // okay
}

Playground link to code

Typescript相关问答推荐

如何使用属性作为具有Generics的TypScript类中的类型守护?

为什么ESLint抱怨通用对象类型?

迁移到Reaction路由V6时,获取模块Reaction-Router-Dom';没有导出的成员RouteComponentProps错误

TypeScrip原始字符串没有方法

TypeScrip:逐个 case Select 退出noUncheck kedIndexedAccess

如何将CSV/TXT文件导入到服务中?

缩小并集子键后重新构造类型

是否存在类型联合的替代方案,其中包括仅在某些替代方案中存在的可选属性?

我可以使用TypeScrip从字符串词典/记录中填充强类型的环境对象吗?

TypeScrip省略了类型参数,仅当传递另一个前面的类型参数时才采用默认类型

使用KeyOf和Never的循环引用

我怎样才能正确地输入这个函数,使它在没有提供正确的参数时出错

打印脚本中正则函数和函数表达式的不同类型缩小行为

错误TS7030:并非所有代码路径都返回值.";由tsfig.json中的";Strong";:False引起

如何正确地将元组表格输入到对象数组实用函数(如ZIP)中,避免在TypeScrip 5.2.2中合并所有值

顶点堆叠的图表条形图未在正确的x轴上显示

如何将对象的字符串文字属性用作同一对象中的键类型

是否使用类型将方法动态添加到类?

TS2532:对象可能未定义

node_modules/@newrelic/native-metrics:命令失败. node_modules/couchbase:命令失败.退出代码:127