Определить тип имущества из другого имущества - PullRequest
0 голосов
/ 25 сентября 2019

Внутри класса я знаю, что свойство будет принадлежать разграниченному объединению на основе другого свойства, но я не могу понять, как заставить TypeScript отражать это.

Я пробовалрасширение различимого объединения, такого как следующее, но это не работает.

type A = { thing: 1, otherThing: string }
type B = { thing: 2, otherThing: number }
type C = A | B;

// ERROR: A class can only implement an object type or intersection
// of object types with statically known members. ts(2422)
class C implements C {
  // ...
}

Метод работает, но этот вопрос состоит в том, чтобы посмотреть, смогу ли я заставить его работать с геттером вместо метода.

class Example {
  thing: 1 | 2;

  get otherThing() {
    if (this.thing === 1) return 'one';
    return 2;
  }

  getOtherThing(thing: 1): string;
  getOtherThing(thing: 2): number;
  getOtherThing(): string | number {
    if (this.thing === 1) return 'one';
    return 2;
  }

  constructor(value: Example['thing']) {
    this.thing = value;
  }

  fails() {
    // I'd really like this type of logic to work
    if (this.thing === 1) {
      // Type '2 | "one"' is not assignable to type 'string'.
      const test: string = this.otherThing;
    }
  }

  works() {
    if (this.thing === 1) {
      const test: string = this.getOtherThing(this.thing);
    }
  }
}

Редактировать: В частности, эта проблема для использования в том же классе.Решение , предоставленное @ jcalz , превосходно, и если то, что я ищу, не может быть сделано, то я мог бы использовать это решение с композицией, чтобы достичь аналогичных результатов.

1 Ответ

0 голосов
/ 25 сентября 2019

Один класс или интерфейс не может расширять или реализовывать объединение вещей, поэтому невозможно достичь того, чего вы хотите напрямую.Если вам действительно нужен один класс для поведения, подобного дискриминационному объединению, я бы подумал об использовании универсального класса, в котором ваше зависимое свойство имеет условный тип :

class Example<T extends C["thing"]> {
  constructor(
    public thing: T,
    public otherThing: Extract<C, { thing: T }>["otherThing"]
  ) {}
}

В конкретном случае, когда свойство дискриминанта thing является допустимым типом ключа (и 1 | 2 работает), вы можете использовать сопоставленных типов вместо условных типов (хотя я будусоздайте отображение условного типа, например:

type MappedOtherThing = {
  [K in C["thing"]]: Extract<C, { thing: K }>["otherThing"]
};

class Example<T extends C["thing"]> {
  constructor(public thing: T, public otherThing: MappedOtherThing[T]) {}
}

Extract<C, {thing: T}>["otherThing"] использует служебный тип Extract, чтобы найти члена C, который может быть назначен{thing: T}, а затем ищет его свойство otherThing.

Любая из этих реализаций даст вам такое поведение:

const eA = new Example(1, "okay"); // Example<1>
eA.otherThing; // string
const a: A = eA; // okay
const eB = new Example(2, 12345); // Example<2>
eB.otherThing; // number
const b: B = eB; // okay
const c: C = Math.random() < 0.5 ? eA : eB; // okay

Надеюсь, что это поможет.Удачи!

Ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...