javascript - Hur man implementerar ett gränssnitt med villkorliga typer i TypeScript


I ett personligt projekt stöter jag på ett problem med TypeScript villkorade typer. Jag har grundläggande förståelse för villkorade typer från TypeScript-dokumentationen. Jag skulle förvänta mig att inställningen nedan fungerar men den ger ett typerrorsmeddelande.

I grund och botten försöker jag definiera ett gränssnitt för en trädnod som laddas från disken. Detta innebär att vissa noder kanske inte är korrekt laddade på grund av saknade filer etc. Så varje nodimplementation innehåller antingen Content eller Error.

interface Content {
  data: number;
}

interface Error {
  code: string;
}

export interface TreeNode<T extends Content | Error> {
  getValue(): T extends Content ? number : string;
}

class ValueNode<ValueType extends Content> implements TreeNode<ValueType> {
  private readonly value: number;
  public constructor(value: number) {
    this.value = value;
  }
 
  public getValue(): number {
    return this.value;
  }
}

Jag skulle förvänta mig att implementeringen av getValue-metoden skulle tillåtas returnera string eftersom gränssnittet implementeras med typ som utökar Content. Men jag får istället detta typfel:

  • Egenskapen getValue i typen ValueNode<ValueType> kan inte tilldelas samma egenskap i basklassen TreeNode<ValueType>.

    • Typen () => number kan inte tilldelas till typen () => ValueType extends Content ? number : string.

      • Typ number kan inte tilldelas typ ValueType extends Content ? number : string
      • Typ () => number kan inte tilldelas typen () => ValueType extends Content ? number : string.
      • Typ number kan inte tilldelas typen ValueType extends Content ? number : string

Tydligt gör jag det på fel sätt. Vad skulle vara det rätta sättet att uppnå mitt mål?

Svar #1

Som @Dai påpekade är det bättre att inte skugga den inbyggda Error-typen (så att du kan använda den i din modul).

Här är två alternativ:

TS Playground-länk

interface ContentValue {
  data: number;
}

interface ErrorValue {
  code: string;
}

export interface ValueGetter<T> {
  getValue(): T;
}

class ValueNode<T extends ContentValue> implements ValueGetter<ContentValue['data']> {
  public constructor(private readonly value: T) {}
 
  public getValue(): number {
    return this.value.data;
  }
}

// or
class SimpleValueNode<T extends ContentValue> {
  public constructor(private readonly value: T) {}
 
  public getValue(): number {
    return this.value.data;
  }
}

Relaterade artiklar