簡體   English   中英

類型保護通用並將其定義為可選

[英]Type guarding a generic and defining it as optional

我有一個接受通用的課程

interface Converter<T = Buffer> {
  uuid: string;
  decode?: (value: Buffer) => T;
  encode?: (value: T) => Buffer;
}

type Converters = Record<string, Converter<any>>;

export default class Service<C extends Converters> {
  private converters?: Converters;

  constructor(converters?: C) {
    this.converters = converters;
  }
}

這個想法是使用構造函數,任何類型的Converter<T>都可以與Service類一起使用。 所以我可以這樣做:

// Converters using Converter<string>
const converters = {
  manufacturer: {
    uuid: "2a29",
    decode: (buffer: Buffer) => buffer.toString()
  },
}

const service = new Service(converters);

這個

// Converters using Converter<number>
const converters = {
  model: {
    uuid: "2a24",
    decode: (buffer: Buffer) => buffer.readInt8(0)
  }
}

const service = new Service(converters);

這個

// Converters using Converter<string> and Converter<number>
const converters = {
  manufacturer: {
    uuid: "2a29",
    decode: (buffer: Buffer) => buffer.toString()
  },
  model: {
    uuid: "2a24",
    decode: (buffer: Buffer) => buffer.readInt8(0)
  }
}

const service = new Service(converters);

或者只是這個

const service = new Service();

因為轉換器是可選的。

現在的問題是, Service類具有一個名為read的方法,該方法將接受一個名為name的參數並返回一個值,並且name的類型和返回類型應基於

  • 哪些轉換器已傳遞給Service
  • 是否有任何轉換器已經傳遞給Service

如果提供了轉換器

  • read應該只接受轉換器的鍵作為name參數
  • read應該返回對應於該名稱的轉換器的泛型類型

如果未傳遞任何轉換器,則read應該接受任何字符串作為參數並返回Buffer

所以如果我通過

const converters = {
  manufacturer: {
    uuid: "2a29",
    decode: (buffer: Buffer) => buffer.toString()
  },
  model: {
    uuid: "2a24",
    decode: (buffer: Buffer) => buffer.readInt8(0)
  }
}

const service = new Service(converters);
const serviceNoConverter = new Service();

這應該發生

const value = service.read("manufacturer");
//    ^^^^^ type string

const value = service.read("model");
//    ^^^^^ type number

const value = service.read("aassd");
//                          ^^^^^ type error, not manufacturer or model

const value = serviceNoConverter.read("asdasdasd")
//    ^^^^^ type Buffer                ^^^^^^^^^ any string is allowed

因此,在StackOverflow的幫助下(尤其是jcalz,感謝伙計們),我做到了:

👇

🔗鏈接到游樂場

此處的所有內容均完全按照說明運行, 您可以在上看到

constructor(converters?: C) {
  this.converters = converters;
}

有一個錯誤的converters不匹配this.converters因為converters不是類型守衛,而this.converters是該類型的Converters 但是我不能將泛型作為

Service<C extends Converters>

因為那樣會破壞read的功能。

所以我的問題是,如何在仍然保留此功能並將其設為可選的同時,對參數進行類型保護?

我認為將C限制為Converters | undefined Converters | undefinedundefined因為默認值應該起作用。 我也建議將成員變量更改為C類型,因為它已受到適當的約束:

class Service<C extends Converters | undefined = undefined> {
   private converters?: C;

   constructor(converters?: C) {
     this.converters = converters;
   }
   //...
}

此處的關鍵是,通過將默認值設置為undefined ,創建不帶轉換器的服務將導致以下類型: Service<undefined>而不是Service<Record<...> | undefined> Service<Record<...> | undefined>

操場

給你

操場上最初的問題是:-過度復雜化。 - converters屬性類型。

正如lukasgeiter指出的那樣, converters應為C類型。但是,不需要默認的通用值undefined 這樣也可以很好地工作:

...
class Service<C extends Converters> {
  private converters?: C;

  constructor(converters?: C) {
    this.converters = converters;
  }

  public read<N extends keyof C>(name: N): C[N] {
      ...
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM