如果你有一个对象类型T
和它的键的联合K
你想要的,你可以写RequireKeys<T, K>
像这样:
type RequireKeys<T extends object, K extends keyof T> =
Required<Pick<T, K>> & Omit<T, K>;
这里我们使用的是Required<T> https://www.typescriptlang.org/docs/handbook/utility-types.html#requiredtype, Pick<T, K> https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys, and Omit<T, K> https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys实用程序类型。这里可能存在边缘情况,例如T
有一个字符串索引签名并且string
出现在里面K
,但初步估计它应该可以工作。
也有点难以理解什么RequiredKeys<Person, "name" | "color">
来自 IDE 中的显示方式:
type VerifiedPerson = RequireKeys<Person, "name" | "color">;
// type VerifiedPerson = Required<Pick<Person, "name" | "color">> &
// Omit<Person, "name" | "color">
如果您希望编译器更明确一点,您可以执行以下操作expand https://stackoverflow.com/questions/57683303/how-can-i-see-the-full-expanded-contract-of-a-typescript-type将类型放入其属性中:
type RequireKeys<T extends object, K extends keyof T> =
(Required<Pick<T, K>> & Omit<T, K>) extends
infer O ? { [P in keyof O]: O[P] } : never;
这导致
/* type VerifiedPerson = {
name: string;
color: string;
address?: string | undefined;
} */
这更容易看到。
--
然后你需要做throwIfUndefined()
a 通用函数 https://www.typescriptlang.org/docs/handbook/2/generics.html这样编译器就可以跟踪之间的关系object
and requiredKeys
传入:
const throwIfUndefined = <T extends object, K extends keyof T>(
object: T,
requiredKeys: readonly K[]
) => {
for (const key of requiredKeys) {
if (!object[key]) throw new Error("missing required key");
}
return object as unknown as RequireKeys<T, K> // need to assert this
};
并测试:
const person: Person = {
...Math.random() < 0.8 ? { name: "Alice" } : {},
...Math.random() < 0.8 ? { color: "Color for a person is problematic" } : {}
};
const requiredKeys = ["name", "color"] as const;
const verifiedPerson = throwIfUndefined(person,
requiredKeys); // possible runtime error here
// const verifiedPerson: RequireKeys<Person, "name" | "color">
如果你想让编译器记住文字类型 https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types "name"
and "color"
是以下组织的成员requiredKeys
那么你需要做类似的事情const断言 https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions (i.e., as const
)这样告诉它。否则requiredKeys
只会是string[]
你会得到奇怪/错误的结果(我们可以防止这些结果,但它可能超出了这里的范围)。
现在,编译器明白了name
and color
被定义,而address
仍然是可选的:
console.log(verifiedPerson.name.toUpperCase() + ": " +
verifiedPerson.color.toUpperCase()); // no compile error
// [LOG]: "ALICE: COLOR FOR A PERSON IS PROBLEMATIC"
verifiedPerson.address // (property) address?: string | undefined