Как бы я принудительно ввел типы с помощью этого заводского шаблона?

#javascript #typescript

#javascript #typescript

Вопрос:

Вот простой пример того, чего я пытаюсь достичь. В этом примере у меня есть несколько классов animal и класс Farm, который служит фабрикой для данного типа animal:

 class Farm<T> {
    farmAnimal: any;
    animals: Array<T> = [];

    constructor(farmAnimal: new() => T, farmSize: number) {
        this.farmAnimal = farmAnimal;

        for(let i =0; i<farmSize; i  ) {
            this.animals.push(new this.farmAnimal());
        }
    }
}

class Pig {
    doThing(){}
}

class Sheep {
    doThing(){}
}
  

Это работает, но моя проблема в том, что я могу сделать что-то подобное:

 let farm = new Farm<Pig>(Sheep, 10);
  

Проблема, похоже, в том, что new() => T тип в конструкторе фермы не обеспечивает принудительное применение переданного типа конструктора.

Есть ли лучший способ сделать это, чтобы TypeScript не воспринимал new Farm<Pig>(Sheep, 10) как допустимый?

Пример: https://www.typescriptlang.org/play?target=1#code/MYGwhgzhAEBiYCcC2AeAKgPmgbwLAChojoAzRJAQQDsBLJMEALmjCoE8BuA4l2 kCMwoIEYNuiwBeaAG0Aul3zdiwAPZUIAFwQBXYJtUIAFGWTU6DZlQCmAdyMBKaJKxoANKXIBlGgC9rVjpIAEbWCE54hDzEmgAWNBAAdKaUfAzOnmZpIIrK0aSGRiDWmtA0zgAMHGUoKT7 1TQA1E0Refkx8UmsFgKJAA46ELFGNrbQcQnJ5Ob8jg6KHdAAvu2r OsEoJAwAAo0AOY47QAmqmjxVAeO2Oub NtQ0F6x1tb9x1FEZxc0Vzd3AgEYqlTTWLQZMZwcgofYHDBGF5vfoeACMFQWBCAA

Комментарии:

1. Исправление синтаксических ошибок, нет, вы не можете сделать, new Farm<Pig>(Sheep, 10); Может быть, опубликовать более полный пример typescriptlang.org/play ? #code/…

2. Я думаю, это потому, что мои классы имеют одинаковую подпись. Смотрите: typescriptlang.org/play ? #code/…

3. Это в любом случае, чтобы избежать этого?

Ответ №1:

Проблема в том, что два класса структурно одинаковы. Typescript использует структурную типизацию, поэтому, если два типа структурно одинаковы, они эквивалентны, это относится даже к классам.

Самый простой способ убедиться, что классы не могут быть псевдонимами, — это добавить личное поле в . Даже если имя такое же, поскольку оно частное, оно не будет структурно эквивалентным.

 class Factory<T> {
    private objs: Array<T> = [];
    private objClass: any;

    constructor(objClass: new () => T) {
        this.objClass = objClass;
        this.objs.push(new this.objClass());
    }
}


class Test{
    private _notAliased: undefined;
    test() {

    }
}

class Test2{
    private _notAliased: undefined;
    test() {
        
    }
}

let p1 = new Factory<Test>(Test2); // error now

  

Ответ №2:

Как вы можете видеть из этой ссылки на typescript playground, то, что вы говорите, неверно. Вы не можете этого сделать new Farm<Pig>(Sheep, 10); .

Вот код, определяющий, когда typescript playground когда-либо отключается:

 class Farm<T> {
  farmAnimal: any;
  animals: Array<T> = [];

  constructor(farmAnimal: new() => T, farmSize: number) {
    this.farmAnimal = farmAnimal;

    for(let i=0; i < farmSize; i  ) {
      this.animals.push(new this.farmAnimal());
    }
  }
}

class Pig {
  doPigThing(): void {

  }
}

class Sheep {
  doSheepThing(): void {

  }
}

const animal = new Farm<Sheep>(Pig, 3); // Compiler error: 
  

Ошибка компилятора:

 Argument of type 'typeof Pig' is not assignable to parameter of type 'new () => Sheep'. Property 'doSheepThing' is missing in type 'Pig' but required in type 'Sheep'.
  

Комментарии:

1. Привет, да, ты прав. Мне следовало проверить свой код, когда я его писал. Похоже, в моем реальном коде проблема в том, что мои классы имеют одинаковую подпись. Это компилируется, несмотря на то, что это разные классы: typescriptlang.org/play ? #code/…

2. Это потому, что ваши классы представляют один и тот же структурный тип. { test: () => void }