2
votes

Type 'never[]' is not assignable to type '...'

I am unable to initialize the property items on my class due to this random error. I figured extending the generic class type to Array<string> would ensure that the type is always a string array?

class MyClass<TItems extends Array<string>> {
    constructor() {
        this.items = [];
    }

    public items: TItems;
}

Gives me the error:

Type 'string' is not assignable to type 'TItems'. 'string' is assignable to the constraint of type 'TItems', but 'TItems' could be instantiated with a different subtype of constraint 'string'.ts(2322)

3
Share your code, not a link to a screenshot of an error in your code.Evert
@Evert Okay, I updated the post.Matt
Great edit. For what it's worth, it wasn't my download.Evert

3 Answers

1
votes

declare array globally inside class

class MyClass{
 never : any[] = [];

 constructor()
 {
  // this.
 } 

}
1
votes

I think you are looking to do this:

class MyClass<TItem extends string> {
    constructor() {
        this.items = [];
    }

    public items: TItem[];
}

The issue is that you are saying that TItems must extend string, which means that this.items must also extend string.

Given that you mentioned you wanted an array of string, this is my best guess.

1
votes

Problem

You might be familiar of the fact that a subtype can be assigned to a supertype but vice-versa is not true. So, in the following code and the inline explanation-

class A extends Array<string> {
  public myProp!: string
}

// it is ok to assign a subtype to its supertype
// because subtype has atleast all those props/methods
// that a supertype has
declare const a1: A
const array1: Array<string> = a1

// it is an error to assign supertype to one of its subtype
// (until they are structurally same)
// because the supertype (array2) may have some missing props/methods
// (myProp in this case) that its subtype has.
declare const array2: Array<string>
const a2: A = array2

Playground

In your code, TItems is a subtype of Array<string>, and type of [] is never[].

If you had typecast-ed it with [] as Array<string>, the supertype (Array<string>) could not be assigned to subtype TItems. Playground

If you had typecast-ed it with [] as TItems, the typecasting is itself erroneous for the very same reason. Playground

Solution

The error could be get ridden of the error with typecasting as-

class MyClass<TItems extends Array<string>> {
    public items: TItems;

    constructor() {
        this.items = [] as unknown as TItems;
    }
}

Playground

But this may result in runtime errors because it's not a "safe" typecasting.

To avoid runtime errors, the correct way is to initialise the prop items with the constructor of the class TItems or function that returns TItems instead of = []. This will eliminate the type errors and also will ensure that there will be no runtime errors. Both ways are demonstrated-

// if a passed TItems is supposed to class
// we pass that constructor of the class 
// constructor of `MyClass`
class MyClass<TItems extends Array<string>> {
    public items: TItems;

    constructor(ctor: new () => TItems) {
        this.items = new ctor();
    }
}

class MyArray extends Array<string> {
  private myProp!: string
}

const myClassVar = new MyClass(MyArray)

Playground

// if a passed TItems is supposed to be just a type
// we pass a function that will create that object of `TItems`
class MyClass<TItems extends Array<string>> {
    public items: TItems;

    constructor(fn: () => TItems) {
        this.items = fn();
    }
}

declare function createObject(): Array<string> & { myProp: string }

const myClassVar = new MyClass(createObject)

Playground