从其他 属性 中确定 属性 的类型

Determine type of property from other property

在 class 内部,我知道 属性 将是基于另一个 属性 的可区分联合之一,但我不知道如何获取 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 {
  // ...
}

该方法有效,但这个问题是看我是否可以使用 getter 而不是方法来使其工作。

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);
    }
  }
}

Edit: Specifically this issue is for use within the same class. The is excellent, and if what I'm seeking cannot be done, then I could use that solution with composition in order to achieve similar results.

单个 class 或接口无法扩展或实现事物的联合,因此无法直接实现您想要的。如果你真的需要一个 class 来给你像受歧视的联盟这样的行为,我会考虑使用 generic class in which your dependent property is of a conditional type:

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

在判别式 属性 thing 是有效键类型(并且 1 | 2 有效)的特定实例中,您可以使用 mapped types 而不是条件类型(虽然我会用这样的条件类型来映射):

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 utility type to find the member of C which is assignable to {thing: T}, and then looks upotherThing 属性.

这些实现中的任何一个都会给你这个行为:

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

希望对您有所帮助。祝你好运!

Link to code