Невозможно использовать частичное для универсального типа - PullRequest
1 голос
/ 04 октября 2019

Я пытаюсь создать класс Map, содержащий сопоставление, которое будет установлено другими классами (например, CharacterManager.
Поскольку содержимое принадлежит другим классам, я хочу, чтобы Mapвозьмите универсальный, который всегда будет расширяться object.

Например:

// Map.ts
interface MapCellData {
  collides: boolean;
}

class Map<CellData extends object = {}> {
  map!: Array<Array<CellData & MapCellData>>;
}

// CharacterManager.ts
interface CharacterManagerCellData {
  character: null | Character;
}

// And so on...

Глядя на тип Array<Array<CellData & MapCellData>>, вы можете видеть, что я хочу каждую ячейку моего 2D-массивачтобы все свойства определялись всеми другими классами.

Однако проблема в том, что теперь я должен предоставить эти свойства при первом заполнении map.
Это не удовлетворяет моим потребностямЯ не хочу, чтобы мой класс Map знал о том, что другие классы хотят добавить. Каждый класс должен отвечать за заполнение своих данных (а Map отвечает за инициализацию map в первый раз, чтобы другиеможет считать его доступным.)

Решение кажется мне довольно простым: поскольку свойства CellData не задаются при первом заполнении карты, я должен сказать, что они являются необязательными. И когда классхочет тебяПохоже, нужно проверить, установлен он или нет.

Исходя из этого, я изменил Array<Array<CellData & MapCellData>> на Array<Array<Partial<CellData & MapCellData>>>. (Только что добавил Partial).

Теперь о реальной проблеме: это не работает, но нет информации об ошибке, я понятия не имею, что я сделал не так.
TypeScript просто заявляет Type '{ collides: true; }' is not assignable to type 'Partial<CellData & MapCellData>'.

Вот полный пример, который выдает ошибку:

interface CharacterManagerCellData {
  character: null | string;
}

interface CollisionCellData {
    collides: boolean;
}

// Directly using partial without generic works fine
type BasicCellData = Partial<CharacterManagerCellData & CollisionCellData>;

const myCell: BasicCellData = {
  collides: true
}

// Using the same pattern with a generic throws a typescript error
function myFunc<CellData extends object = {}>() {
  type RealCellData = Partial<CellData & CollisionCellData>;

  const cell: RealCellData = {
    collides: true
  }
}

Я использую последнюю версию TypeScript (3.6.3), и эта ошибка также возникает на детской площадке

1 Ответ

2 голосов
/ 04 октября 2019

Typescript не ошибся в ошибке. Вы никогда не можете безопасно назначить конкретное значение универсальному типу. Рассмотрим следующий пример:

myFunc<{ collides: false }>()

collides должно быть false, а не true с учетом переданного типа, но вы инициализируете его с true. Это явно неправильно. Универсальная функция должна быть действительной для любого с учетом T. Это ограничение означает, что создание экземпляра объекта и присвоение ему ссылки универсального типа обычно невозможно (в этом случае возможен пустой объект, поскольку Partial делает все необязательным независимо от типа const cell: RealCellData = {})

Тамявляются пробелами для этого ограничения, например, допустимы присваивания, но для универсальных типов, если типскрипт не может быть уверен, каков окончательный тип свойства, он не допустит даже простых назначений:

// this is better
function myFunc<CellData extends CollisionCellData >() {
  type RealCellData = Partial<CellData>;

  const cell: RealCellData = { //This is still an error
     collides: true
  } 
  cell.collides = true; // this is ok beacuse ts knows collide is boolean
}


//this is not ok 
function myFunc<CellData extends object >() {
  type RealCellData = Partial<CellData & CollisionCellData>;

  const cell: RealCellData = { //This is still an error
     collides: true
  } 
  cell.collides = true; // this is not ok, beacuse TS does not know if CellData has collides and what it's type may be
}

Есливы согласны с некоторыми типами, создающими несоответствия в ваших типах, вы можете использовать утверждение типа:

const cell = {
  collides: true
} as RealCellData

Play

...