按照文档映射类型,在 TypeScript 中应该可以有这样的包装器:
export type Wrapped<T> = {
[P in keyof T]: T[P];
} & { _state: number }
function wrap<T extends object>(x: T): Wrapped<T> {
let xWrapped = x as Wrapped<T>
xWrapped._state = 0;
return xWrapped;
}
总的来说,这似乎运作良好,Wrapped<T>
行为完全像T
.
然而,我注意到,在某些情况下,类型系统可能会不满意。例如:
let a = new Date()
let b = wrap(a)
function f(d: Date) {}
f(a) // works
f(b) // error: Property '[Symbol.toPrimitive]' is missing in type 'Wrapped<Date>' but required in type 'Date'
我该如何编写类型定义Wrapped<T>
支持类似的类型Date
?
背景:这个简化示例背后的实际问题出现在固体(框架),它使用这种包装器的递归版本其核心状态处理。在这种情况下,问题非常严重,因为 TypeScript 不接受Wrapped<MyState>
for a MyState
一旦处于嵌套状态的某个位置,就会出现“不可包装”类型,例如Date
, 导致... as any as X
无处不在。这个问题的目标是让 TypeScript 更容易使用 Solid。
我尝试过的:我已经对此进行了几周的修改,但没有成功。这2.9 发行说明提到映射类型{ [P in K]: XXX }
现在也支持符号,但我不知道它在语法上看起来如何。
export type Wrapped<T> = {
[P in keyof T]: T[P];
} & {
[symbol in keyof T]?: T[symbol]; // doesn't work
} & { _state: number }
“有效”是将包装器写为
export type Wrapped<T> = {
[P in keyof T]: T[P];
} & { _state: number } & {
[Symbol.toPrimitive](hint: "string"): string;
[Symbol.toPrimitive](hint: "default"): string;
[Symbol.toPrimitive](hint: "number"): number;
}
但显然我不想无条件地将这些符号添加到所有包装类型中。基本上我正在寻找表达“如果符号在 T 中,则将其添加到包装类型”的语法。如果这通常不可能,是否至少可以针对一组众所周知的符号对其进行硬编码?
我在 GitHub 上发现了一些可能相关的问题,但作为 TypeScript 初学者,我无法理解它们:
- #1863 允许使用符号进行索引
- #24587 类型“symbol”不能用作索引类型。
- #4538 改进对 Symbol.toPrimitive 的支持