xhd2015 / coverage-visualizer

a highly customizable test coverage visualizer
MIT License
2 stars 0 forks source link

TS Error: MyItem is assignable to the constraint of type T, but T could be instantiated with a different subtype of constraint #1

Open xhd2015 opened 3 months ago

xhd2015 commented 3 months ago

I have a function named copyItem, which accepts a generic constrained param T. But when it is called with another generic type, ts emits the error as described in the title.

Function definition:

function copyItem<T extends { children: T[] }>(data: T): T {
    return data
}

Test Code:

interface MyItem {
    children: MyItem[]
}

function testCopyItem<T extends MyItem>(data: T) {
    copyItem(data)
}

Got this error:

Argument of type 'T' is not assignable to parameter of type '{ children: T[]; }'.
  Type 'MyItem' is not assignable to type '{ children: T[]; }'.
    Types of property 'children' are incompatible.
      Type 'MyItem[]' is not assignable to type 'T[]'.
        Type 'MyItem' is not assignable to type 'T'.
          'MyItem' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'MyItem'.ts(2345)

The workaround I came up with is to declare an extra generic type V, which extends T, and replace T with V in the code, demonstrated as below:

function copyItemV2<V extends T, T extends { children: T[] }>(data: V): V {
    return data
}

Test OK Now:

// ok
function testCopyItem<T extends MyItem>(data: T) {
    copyItemV2(data)
}

(The whole example can be found at TS Playground.)

But why?

xhd2015 commented 3 months ago

Asked on stackoverflow: https://stackoverflow.com/questions/78640175

xhd2015 commented 3 months ago

But it seems the error is not caused by returning, but that data type is incompitable with T:

function findData<T extends { children?: T[] }>(data: T){
}

interface MyItem {
    children: MyItem[]
}

// bad
function testFind<T extends {children: T[]}>(data: T) {
    findData(data)
}

// good
function testFindV2<T extends MyItem>(data: T) {
    findData(data)
}

But if define MyItem as :

interface MyItem<T extends {children:T[]}> {
    children:  T[]
}

Everything works fine.