尝试分配接口时出错

Error when trying to assigning interface

我有以下 classes/interfaces:

class Klass {
  baseAttr: string;
}

class ChildKlass extends Klass {
  childAttr: string;
}

interface T1<T extends Klass = Klass> {
  readonly display: ReadonlyArray<keyof T>;
}

class T1Klass implements T1<ChildKlass> {
  readonly display: ReadonlyArray<keyof ChildKlass> = ['childAttr'];
}

以上一切正常...


当我尝试创建一个 "dictionary" 值应该是:

时出现问题

t1 - "an instance of class that implements T1";
klass - "an instance of class that extends Klass"

interface ConstructableT1 {
  new(): T1;
}

interface T1KlassDictionary {
  [key: string]: {
    // Since I can't just say t1 is typeof T1, I created the ConstructableT1 interface
    t1?: ConstructableT1,
    klass?: typeof Klass
  };
}

当我尝试实例化一个 object:

const dictionary: T1KlassDictionary = {
  test: {
    t1: T1Klass,
    klass: ChildKlass
  }
};

...它给了我以下错误:

Type 'typeof T1Klass' is not assignable to type 'ConstructableT1'.

Playground link

我做错了什么?

Type 'typeof T1Klass' is not assignable to type 'ConstructableT1'.

TypeScript 编译器有时会缩短错误消息。在这种情况下,它没有说明为什么 typeof T1Klass 不能分配给 ConstructableT1.

要了解原因,让我们尝试将一个分配给另一个:

let a: typeof T1Klass;
let b: ConstructableT1 = a;

现在我们得到

Type 'typeof T1Klass' is not assignable to type 'ConstructableT1'.
  Type 'T1Klass' is not assignable to type 'T1<Klass>'.
    Types of property 'display' are incompatible.
      Type 'ReadonlyArray<"id" | "attr">' is not assignable to type 'ReadonlyArray<"attr">'.
        Type '"id" | "attr"' is not assignable to type '"attr"'.
          Type '"id"' is not assignable to type '"attr"'.

这似乎是合理的 - 一个可能的键值 - "id" - 不可分配给文字类型的值 "attr"。

一个可能的解决方案是使 interface ConstructableT1 成为泛型,这样编译器就会知道 t1 的实际类型,并且不会对不兼容的泛型参数 (T1Klass) 使用默认值:

interface T1<T extends Klass = Klass> {
    readonly display: ReadonlyArray<keyof T>;
}

interface ConstructableT1<T extends Klass = Klass> {
    new(): T1<T>;
}



class T1Klass implements T1<ChildKlass> {
    readonly display: ReadonlyArray<keyof ChildKlass> = ['id'];
}


class Klass {
    attr: string;
}

class ChildKlass extends Klass {
    id: string;
}

interface T1KlassDictionary {
    [key: string]: {
        t1?: ConstructableT1<ChildKlass>,
        klass?: typeof Klass
    };
}

const dictionary: T1KlassDictionary = {
    test: {
        t1: T1Klass,
        klass: ChildKlass
    }
};

更新 问题已澄清:

The problem comes when I try to create a "dictionary" where the value should be:

t1 - "an instance of class that implements T1"; klass - "an instance of class that extends Klass"

那我会说你 t1klass 的类型有误。您将它们声明为构造函数 - 也就是说,dictionary.t1 可用于创建这样的实例

const inst = new dictionary.test.t1();

playground 说这样创建的 inst 的类型是 const inst: T1<ChildKlass>.

如果字典应该包含实例,应该这样声明

interface T1KlassDictionary {
    [key: string]: {
        t1?: T1<ChildKlass>,
        klass?: Klass
    };
}

并使用 new()

创建的实例进行初始化
const dictionary: T1KlassDictionary = {
    test: {
        t1: new T1Klass(),
        klass: new ChildKlass()
    }
};

然后您可以访问实例属性:

const attr = dictionary.test.klass.attr; 
// but no id because declared type of `klass`
//  is `Klass` which does not have `id`

const attr = dictionary.test.t1.display;