不判断K extends "totals"[]
,而是交换它并判断元素类型,而不是数组类型:"totals" extends K[number]
.有时很难考虑使用联合,但请记住,当您扩展一个联合时,您将使联合的成员数量增加reduce;"totals"
是"totals" | "body"
的子类型,但"totals" | "body"
不是"totals"
的子类型.
如果你做到了这两件事,它就会奏效:
type Keywords = "totals" | "body"; // ...and more
type Model<K extends Keywords[] = []> = {
id: number;
}
& ("totals" extends K[number] ? { total: number; amount: number } : {})
& ("body" extends K[number] ? { body: string; name: string } : {});
const m: Model<["totals", "body"]> = { id: 1 }; // Missing properties error as desired
Playground link个
次要问题(不是那么重要,但需要解决):
const words: Keywords[] = ['totals']
在那里,words
的类型是just,Keywords[]
,而不是具体的"totals"[]
,所以它太开放了.你可以使用"totals"[]
,但这是多余的,理想情况下,我们想让TypeScrip为我们推断出该类型.要做到这一点,我们有两个 Select :as const
或更新的satisfies
(感谢您提醒我):
const words = ["totals"] as const;
// or
const words = ["totals"] satisfies Keywords[];
两者的结果都是words
的类型为"totals"[]
,但是satisfies
给了我们更好的开发体验,如果我们在数组中包括不在Keyword
中的元素,它会警告我们,所以这可能就是我们想要的.
由于这会创建只读类型,因此需要将类型参数约束从Model
更新为K extends readonly Keywords[]
:
type Keywords = "totals" | "body"; // ...and more
type Model<K extends readonly Keywords[] = []> = {
// −−−−−−−−−−−−−−−−−−^^^^^^^^
id: number;
}
& ("totals" extends K[number] ? { total: number; amount: number } : {})
& ("body" extends K[number] ? { body: string; name: string } : {});
const words = ["totals"] satisfies Keywords[];
// ^ ^^^^^^^^^^^^^^^^^^^^
const m2: Model<typeof words> = { id: 1 }; // Missing properties error as desired
Playground link