如果您的接口不符合所需的约束,您可以使用一些辅助类型来触发编译器错误。它看起来像这样:
type Extends<T extends U, U> = void;
type MutuallyExtends<T extends U, U extends V, V = T> = void;
type CheckKeysOfActionPayloads =
MutuallyExtends<keyof ACTION_PAYLOADS, ACTION_NAMES>; // ok
type CheckValuesOfActionPayloads =
Extends<ACTION_PAYLOADS[ACTION_NAMES], SOME_TYPE>; // ok
这里,CheckKeysOfActionPayloads 仅在 ACTION_PAYLOADS 的键和 ACTION_NAMES 类型相互扩展时才能编译...对于文字联合,这意味着它们需要相同。
只有当ACTION_PAYLOADS(在ACTION_NAMES键处)的值可分配给SOME_TYPE时,CheckValuesOfActionPayload才会编译。
为了具体起见,我将把 SOME_TYPE 更改为可能失败的东西,并且让我们确保 ACTION_NAMES 中包含 "ACTION_2"(与您的示例代码相反):
const ACTION_NAMES = ["ACTION_1", "ACTION_2", "ACTION_3"] as const;
type ACTION_NAMES = typeof ACTION_NAMES[number];
type SOME_TYPE = {
[key: string]: string | number | boolean
}
让我们看看如果我们搞砸了ACTION_PAYLOADS 会发生什么。首先,如果我们漏掉了一个键:
interface ACTION_PAYLOADS {
ACTION_1: { propA: string, propB: number },
// where's ACTION_2?
ACTION_3: { propD: string }
}
type CheckKeysOfActionPayloads =
MutuallyExtends<keyof ACTION_PAYLOADS, ACTION_NAMES>; // error!
// ----------------------------------> ~~~~~~~~~~~~
// Type "ACTION_2" is not assignable to type "ACTION_1" | "ACTION_3"
或者如果我们有一个额外的密钥:
interface ACTION_PAYLOADS {
ACTION_1: { propA: string, propB: number },
ACTION_2: { propC: boolean },
ACTION_3: { propD: string },
ACTION_4: { propE: number } // what's this?
}
type CheckKeysOfActionPayloads =
MutuallyExtends<keyof ACTION_PAYLOADS, ACTION_NAMES>; // error!
// ---------------> ~~~~~~~~~~~~~~~~~~~~~
// Type "ACTION_4" is not assignable to type "ACTION_1" | "ACTION_2" | "ACTION_3"
最后,如果键正确但值错误:
interface ACTION_PAYLOADS {
ACTION_1: { propA: string, propB: number },
ACTION_2: { propC: Date }, // what's this?
ACTION_3: { propD: string }
}
type CheckValuesOfActionPayloads =
Extends<ACTION_PAYLOADS[ACTION_NAMES], SOME_TYPE>; // error!
// -------> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Type 'Date' is not assignable to type 'string | number | boolean'
您可以看到,在所有这些情况下,您都会收到一个错误,希望可以用来帮助您修复代码。
Playground link to code