从某种意义上说,您正在寻找类型级别satisfies操作员。如果你写e satisfies T
where e
是一些表达和T
是某种类型,编译器将确保e
is 可分配的 to T
没有widening to T
, so e
保持其原始类型,但如果与以下内容不兼容,则会收到错误消息T
。您想做同样的事情,但用另一种类型替换表达式。就像是
// this is invalid TS, don't do this:
type MappedConfigurationTypes = {
test: testType;
mock: MockType
} Satisfies {[K in ConfigurationTypes]: any}
但没有这样的Satisfies
类型运算符。太糟糕了。
幸运的是,我们基本上可以自己构建一个:而不是T Satisfies U
,我们可以写Satisfies<U, T>
(我正在制作“Satisfies U
“注意的句法单位,所以这就是我想要的原因Satisfies<U, T>
并不是Satisfies<T, U>
。但您可以根据需要定义它)。
定义如下:
type Satisfies<U, T extends U> = T;
你可以看看如何Satisfies<U, T>
将始终评估为T
, 但是由于T
is 受约束的 to U
,编译器会抱怨如果T
不兼容U
.
让我们尝试一下:
type ConfigurationTypes = 'test' | 'mock';
type MockType = { id: string }
type TestType = { code: string }
type MappedConfigurationTypes = Satisfies<{ [K in ConfigurationTypes]: any }, {
test: TestType
mock: MockType
}>
看起来不错。如果您将鼠标悬停在MappedConfigurationTypes
你看它相当于
/* type MappedConfigurationTypes = {
test: TestType;
mock: MockType;
} */
另一方面,如果您将另一个成员添加到ConfigurationTypes
union,您将看到所需的错误:
type ConfigurationTypes = 'test' | 'mock' | 'oops'
type MappedConfigurationTypes = Satisfies<{ [K in ConfigurationTypes]: any }, {
test: TestType
mock: MockType,
}> // error!
// Property 'oops' is missing in type '{ test: TestType; mock: MockType; }' but required
// in type '{ test: any; mock: any; oops: any; }'.
Playground 代码链接