从某种意义上说,您正在寻找类型级别的satisfies operator。如果你写 e satisfies T,其中 e 是某种表达式,T 是某种类型,编译器将确保 e 是可分配的到 T 没有拓宽到 T,所以 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 是 constrained 到 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 link to code