有条件可选属性(对象类型和接口中)
🔍 搜索词
- 有条件可选
- 有条件可选
- 有条件只读
- 有条件只读
这些问题相关,但并不完全相同:
- Allow conditionally setting optional properties in a mapped type #36126
- Tuple with conditional optional type #31409
✅ 可实现性检查清单
我的建议满足以下准则:
- 这不会对现有的TypeScript/JavaScript代码造成破坏性的改变
- 这不会改变现有JavaScript代码的运行时行为
- 这可以在不根据表达式的类型发出不同的JS的情况下实现
- 这不是一个运行时特性(例如库功能、带有JavaScript输出的非ECMAScript语法、新的JS语法糖等)
- 这个特性将与 TypeScript's Design Goals 的其他部分一致。
⭐ 建议/📃 动机示例
一种新的语法和关联的类型检查使得可以将属性标记为“有条件可选”,例如:
interface Calculate<T> {
items: T[];
/**
* Converts an item into a string for comparison.
* If `T` is already a string type, this is optional.
*/
getKey(? if T extends string): (item: T) => string;
}
这里的意图是,如果 getKey
是 string
或者是 string
的子类型,那么 getKey
是可选的;否则,getKey
是强制性的。因此,一个实现(例如)可以提供一个默认实现 getKey = str => str
。
目前可以使用混合交集和有条件Map类型来编写实现此功能的类型。然而,你会失去某些重要的人体工程学属性:
- 交集类型、Map类型和有条件类型的语法比接口/对象类型复杂得多,也不如它们熟悉
- 有条件类型和复杂的Map很可能会丢失jsdoc注解,导致开发者在稍后尝试使用该类型时无法获得智能感知
- 复杂的类型无法像接口和对象类型那样进行扩展
💻 用例
一般来说,属性的任何属性都可以以相同的方式进行有条件处理:
type Example = <T, Flag extends boolean, Active extends boolean> = {
// conditionally optional:
convertToString (? if T extends string): (item: T) => string;
// optional; made conditionally required:
initialValue? (-? if T extends boolean): T;
// conditionally readonly:
(readonly if Flag extends "permanent") flagValue: boolean;
// conditionally defined:
(parentId if Active extends true): string;
}
在大多数情况下,存在一个基本(但不完整的)解决方案:在所有地方都使用较为宽松的形式。例如,如果它主要被使用并且你不想忘记传递一个属性,只需将其设置为必需;如果它主要被访问并且你不想忘记属性存在,只需将其设置为可选等。
允许这些值以有条件的方式设置将只是更容易表达某些更复杂的特定领域约束,同时保持类型主要自包含且易于阅读。
6条答案
按热度按时间kzmpq1sx1#
这似乎至少在组合爆炸性方面(也许无法判定?)当从一个具体类型进行推断到
Calculate<T>
时,因为你可以写一些类似的东西如果对这个问题的答案是“实际上,这些条件将非常少”,那么它似乎不像
CalcString | CalcNonString<T>
那样获得很多好处。vtwuwzda2#
我没有考虑递归约束 - 对于我的目的,我肯定不需要这些,所以拒绝它们是可以的。这将与编写
一个具体的例子,显然不能重写为仅仅是一个联合,将是类似于:
你不能写
并且编写
破坏了拥有
ForAny
的目的。因此,您可以编写我不能仅仅使用一个非通用的
ForString
,因为还有其他字段也引用了T
,这可能比string
更具体(例如,联合、枚举或带标签的模板字面量类型)。或者更一般地,带有具有某些属性或其他约束的对象。pgvzfuti3#
我非常赞成这个功能,尽管可能只是这个功能的子集。目前,解决方案需要切换到
type
(而不是interface
),这会破坏其他事物。我的用例是根据类型参数拥有两个"侧面"。
例如:
lb3vh1jj4#
也适用于:
而不是
gpnt7bae5#
当前的解决方案确实看起来相当丑陋。
它还迫使您将接口重写为具有交集的类型。
xoefb8l86#
我正在寻找一些简单、人类可读且肉眼易懂的参数类型,以便在类型上提供一个可用的参数:
使用你的“避免而不是”,我实现了这个:
当然,它已经用于其他目的了,对我来说,这个类型本身是一个函数类型的生成器,
而且我想通过IDE提供更好的自动建议,并防止开发者在某些情况下传递不应该传递的参数。