递归泛型类型来扩展类型 - 奇怪的行为,这是一个错误吗?

Posted

技术标签:

【中文标题】递归泛型类型来扩展类型 - 奇怪的行为,这是一个错误吗?【英文标题】:Recursive generic type to extend types - weird behaviour, is this a bug? 【发布时间】:2019-05-07 08:48:32 【问题描述】:

给定一个接口

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&lt;boolean&gt;(true) 的上下文接口现在也会引发以下 TS 错误:

TS2322: Type 'ContextualData<boolean>' is not assignable to type 
'boolean | ContextualData<true> | ContextualData<false>'.

我错过了什么?这是一个错误吗?

【问题讨论】:

我认为您遇到了这个问题:typescriptlang.org/docs/handbook/release-notes/… ...如果您正在处理string,您的示例就可以工作,因为这确实是一个原始类型,但因为boolean实际上是true | false 的联合类型,条件类型将其分配给truefalse 类型的整个变体。因此,如果您将其更改为ContextualData&lt;true&gt;(true),它也可以工作(并且Test 类型的内部值也必须是true 类型)。我玩过它,但没有找到解决 boolean 【参考方案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),
    ,

【讨论】:

以上是关于递归泛型类型来扩展类型 - 奇怪的行为,这是一个错误吗?的主要内容,如果未能解决你的问题,请参考以下文章

boost::variant:递归向量类型的奇怪行为

NSManagedObjectContext 扩展中泛型函数中的奇怪 Swift 行为

递归泛型类型的实例化速度越慢,嵌套越深。为啥?

扩展泛型类型 - PriorityQueue

扩展类型的泛型和 Typescript 中的普通类型有啥区别?

泛型编程的术语