提供界面
interface Test {
inner: {
value: boolean,
}
}
和一个班级
class ContextualData<T> {
constructor(public data: T) {}
}
我希望能够这样做:
const original: Test = {
inner: {
value: true,
},
}
// Wrap the value in a ContextualData object.
original.inner.value = new ContextualData<boolean>(original.inner.value)
我试图通过声明以下类型来实现:
export type Primitive = undefined | null | boolean | string | number | Function
export type Contextuable<T> = T | ContextualData<T>
export type DeepContextuable<T> =
T extends Primitive ? Contextuable<T> : DeepContextuableObject<T>
export type DeepContextuableObject<T> = {
[K in keyof T]: DeepContextuable<T[K]>
}
,然后使用DeepContextual
转换我的Test
界面:
const original: DeepContextual<Test> = {
inner: {
value: new ContextualData<boolean>(true),
},
}
这很好。
现在,让我们向ContextualData
类添加另一个方法:
class ContextualData<T> {
constructor(public data: T) {}
public map<U>(mapFn: (current: T) => U): U {
return mapFn(this.data)
}
}
即使不使用新功能,带有value: ContextualData<boolean>(true)
的上下文接口现在也会引发以下TS错误:
TS2322: Type 'ContextualData<boolean>' is not assignable to type
'boolean | ContextualData<true> | ContextualData<false>'.
我想念什么?这是错误吗?
答案 0 :(得分:1)
您遇到了条件类型的分布行为。这种(非常有用的)行为指示条件类型分布在裸类型参数的联合成员上。将其与Typescript将boolean
视为true | false
的事实配对,我们得到了。
DeepContextuable<boolean> = DeepContextuable<true | false>
= DeepContextuable<true> | DeepContextuable<false>
= (true | Contextuable<true>) | (false | Contextuable<false>)
= boolean | Contextuable<true> | Contextuable<false
此行为仅在裸类型参数上发生。要禁用它,我们可以将参数放在一个元组中,一切将按您期望的那样进行。
export type Primitive = undefined | null | boolean | string | number | Function
export type Contextuable<T> = T | ContextualData<T>
export type DeepContextuable<T> =
[T] extends [Primitive] ? Contextuable<T> : DeepContextuableObject<T>
export type DeepContextuableObject<T> = {
[K in keyof T]: DeepContextuable<T[K]>
}
interface Test {
inner: {
value: boolean,
}
}
class ContextualData<T> {
constructor(public data: T) { }
public map<U>(mapFn: (current: T) => U): U {
return mapFn(this.data)
}
}
const original: DeepContextuable<Test> = {
inner: {
value: new ContextualData<boolean>(true),
},
}