
在开发过程中,我们经常会遇到需要定义一个对象,其中某些属性的值(例如一个字符串数组)必须与对象内另一个属性(例如另一个字符串数组)中的值保持一致。考虑一个场景,我们有一个 OrderedProperties 对象,它包含:
例如:
const a: OrderedProperties = {
props: ['title', 'firstName', 'lastName', 'nickName'],
order: [
'title',
['firstName', 'lastName'],
'nickName'
]
}最初的类型定义可能如下所示:
export type OrderGrid = Array<string | [string, string]>;
export type OrderedProperties = {
props: string[];
order: OrderGrid
};然而,这种定义存在一个关键问题:order 数组中的字符串类型是 string,它并不强制要求这些字符串必须是 props 数组中已声明的属性名称。这意味着开发者可能会在 order 中引用一个在 props 中不存在的属性名,而 TypeScript 编译器无法捕获这类错误,从而可能导致运行时问题。
为了解决上述问题,我们可以利用 TypeScript 的泛型(Generics)来创建类型参数,并将这些参数作为约束,以确保 order 数组中的字符串严格匹配 props 数组中定义的属性。
首先,我们将 OrderGrid 类型改造为泛型类型,接受一个类型参数 S,它必须是 string 的子类型。这样,OrderGrid 中的所有字符串元素都将被约束为 S 类型。
type OrderGrid<S extends string> = Array<S | [S, S]>;
这里的 S extends string 表示 S 是一个字符串字面量类型,例如 "title" | "firstName"。
接下来,我们对 OrderedProperties 类型进行泛型化。它将接受两个类型参数:
type OrderedProperties<P extends string, O extends P = P> = {
props: P[];
order: OrderGrid<O>;
};通过这种方式,我们建立了 props 和 order 之间的动态类型关联。
现在,当创建 OrderedProperties 类型的对象时,我们可以显式地指定 P 类型参数,从而让 TypeScript 强制执行类型检查。
// 正确示例:所有order中的属性都在props中定义
const a: OrderedProperties<"firstName" | "lastName" | "nickName" | "title"> = {
props: ["title", "firstName", "lastName", "nickName"],
order: [
"title",
["firstName", "lastName"],
"nickName",
],
}; // 类型检查通过
// 错误示例:props中缺少"title",或者order中使用了props未定义的属性
const a2: OrderedProperties<"firstName" | "lastName" | "nickName"> = {
props: ["title", "firstName", "lastName", "nickName"], /* 错误:
~~~~~~~
类型 '"title"' 不能赋值给类型 '"firstName" | "lastName" | "nickName"'。(2322) */
order: [
"title", /* 错误:
~~~~~~~
类型 '"title"' 不能赋值给类型 '"firstName" | "lastName" | "nickName" | ["firstName" | "lastName" | "nickName", "firstName" | "lastName" | "nickName"]'。(2322) */
["firstName", "lastName"],
"nickName",
],
};在 a2 的例子中,由于我们显式地将 P 类型参数限制为 "firstName" | "lastName" | "nickName",当 props 数组中包含 "title" 或 order 数组中包含 "title" 时,TypeScript 就会立即报告类型错误。
显式地枚举所有允许的字符串字面量可能会很繁琐。如果 OrderedProperties 对象通常作为函数参数使用,我们可以利用 TypeScript 的类型推断能力来简化这一过程。
通过将 OrderedProperties 对象作为泛型函数的参数,编译器可以自动从传入的 props 数组中推断出 P 的具体类型,并进一步约束 order 数组。
declare function handleOrderedProps<P extends string, O extends P = P>(
props: OrderedProperties<P, O>,
): void;
// 正确示例:编译器自动推断 P 为 "title" | "firstName" | "lastName" | "nickName"
handleOrderedProps({
props: ["title", "firstName", "lastName", "nickName"],
order: [
"title",
["firstName", "lastName"],
"nickName",
],
}); // ok
// 正确示例:order中可以不包含所有props中的属性
handleOrderedProps({
props: ["title", "firstName", "lastName", "nickName"],
order: [
"title",
["firstName", "lastName"],
],
}); // "nickName" 未被使用,但类型上是允许的 (ok)
// 错误示例:order中包含props未定义的属性
handleOrderedProps({
props: ["title", "firstName", "lastName"],
order: [
"title",
["firstName", "lastName"],
"nickName", /* 错误:
~~~~~~~~~~
类型 '"nickName"' 不能赋值给类型 '"firstName" | "lastName" | "title" | ["firstName" | "lastName" | "title", "firstName" | "lastName" | "title"]'。(2322) */
],
});在这个 handleOrderedProps 函数的例子中,TypeScript 能够根据 props 数组的内容自动推断出 P 的精确字面量类型,然后将这个类型用于约束 order 数组中的元素。这样,开发者无需手动指定泛型参数,即可享受到严格的类型检查。
在某些设计中,props 数组和 order 数组之间可能存在冗余。如果 props 数组仅仅是 order 数组中所有唯一属性名的扁平化集合,那么 props 属性本身可能就是冗余的,或者可以从 order 动态派生出来。
我们可以编写一个辅助函数来从 order 数组中提取并扁平化所有属性名:
/**
* 从 OrderGrid 中提取所有唯一的属性名称。
* @param order - 描述属性排列方式的 OrderGrid 数组。
* @returns 包含所有属性名称的字符串数组。
*/
function getPropsFromOrder<S extends string>(order: OrderGrid<S>): S[] {
// 使用 flat() 方法将嵌套数组扁平化,并进行类型断言以保持 S 类型
return order.flat() as S[];
}
// 示例使用
const myOrderGrid: OrderGrid<"a" | "b" | "c"> = ["a", ["b", "c"]];
const derivedProps = getPropsFromOrder(myOrderGrid); // derivedProps 的类型是 ("a" | "b" | "c")[]
console.log(derivedProps); // 输出: ["a", "b", "c"]这个 getPropsFromOrder 函数展示了如何从 order 属性中派生出 props 属性,从而减少数据冗余并确保一致性。在实际应用中,你可以根据业务逻辑决定 props 是否应该显式声明,或者由 order 派生。
通过利用 TypeScript 的泛型和类型约束,我们可以有效地解决对象属性之间动态匹配的类型安全问题。
关键点回顾:
注意事项:
通过掌握这些技术,你将能够构建出更加健壮、可维护且类型安全的 TypeScript 应用程序。
以上就是TypeScript 中如何强制对象属性间的动态匹配的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号