簡體   English   中英

typescript 中的 Generics - 不可分配給類型/類型參數

[英]Generics in typescript - not assignable to type / parameter of type

我有以下兩個服務調用,它們都使用方法 ConcertDates()。

private callMyFirstService(params: MyParams): Observable<MyDto> {
    return this.http.post<MyDto>(params).pipe(map(response => this.convertDates<MyDto>(response)));
}

private callMySecondService(params: MyOtherParams): Observable<MyAnotherDto> {
    return this.http.post<MyAnotherDto>(params).pipe(map(response => this.convertDates<MyAnotherDto>(response)));
}

由於這些方法與另一種類型的 dto 一起運行,因此我嘗試實現一些在這兩種情況下都可以調用的通用函數。 我有類似的東西:

private convertDates<type>(input: type): type {
    for (const key in input) {
        if (!input.hasOwnProperty(key)) {
            continue;
        }

        if (typeof input[key] === 'object') {
            this.convertDates(input[key]);
        } else if (typeof input[key] === 'string' && this.MY_REGEXP.test(input[key])) {
            input[key] = new Date(input[key]);
        }
    }
    return input;
}

這似乎不起作用,因為我總是得到:

Type 'Date' is not assignable to type 'type[Extract<keyof type, string>]'

和...

Argument of type 'type[Extract<keyof type, string>]' is not assignable to parameter of type 'string'.
  Type 'type[string]' is not assignable to type 'string'.

有任何想法嗎? 提前非常感謝!

無論該type是什么類型,都具有特定的形狀。 您似乎期待 object 具有一些字符串屬性,而您將其更改Date 這意味着您違反了該類型。

或者更簡單地說:

const obj = { maybeDate: 'foo' } // type is { maybeDate: string }
obj.maybeDate = new Date() // error

maybeDate的類型是string ,因此您不能只將Date分配給該插槽。 這違反了類型。


通常在進行這樣的轉換時,您需要將 object 轉換為中間類型,然后將其轉換為最終類型。

const obj = { maybeDate: 'foo' } // type is { maybeDate: string }
const objIntermediate = obj as { maybeDate: string | Date }
objIntermediate.maybeDate = new Date()
const objFinal = objIntermediate as { maybeDate: Date }

操場


在這一點上,重要的是要說你的邏輯是不可能輸入的。 首先,因為 typescript 不支持正則表達式轉換,但更重要的是類型系統無法知道是否應將string轉換為日期,因為它不會提前知道該字符串的內容。

interface MyDto {
  name: string
  createdAt: string // How could typescript possible know this is a date string?
}

我以不同的方式在自己的代碼庫中解決了問題。 我鍵入Dto以使字段為Date ,例如:

interface MyDto {
  name: string
  createdAt: Date
}

然后使用一些自定義 JSON 解碼器邏輯來更改字段:

/** Parse JSON content from a response. If any string matches the ISO date time format, convert it to a Date. */
async function decodeJson(res: Response): Promise<any> {
  return JSON.parse(await res.text(), (key: string, value: unknown) => {
    if (typeof value === 'string' && dateFormat.test(value)) {
      return new Date(value)
    }
    return value
  })
}
const dateFormat = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/

由以下人員調用:

function fetchStuff<T>(path: string) {
  const res = await window.fetch(path)
  const json = await decodeJson(res)
  return json as T
}

// Then in some async function
const myStuff = await fetchStuff<MyDto>('/api/foo')
myStuff.createdAt.getHours() // works

這是有效的,因為響應在轉換為泛型類型之前是any的,因此 typescript 讓您可以對它做任何事情。 (老實說,我不確定如何使其適應 rxjs 供電代碼)

操場

這種自動轉換日期的整個方法的最大缺點是,如果有人在某個文本字段中手動鍵入日期之類的字符串,它將被轉換為日期,但是您的 Dto 會將其鍵入為字符串,這可能會崩潰你的申請。 例如,有人輸入他們的名字為2015-01-02 在我的應用程序中,這是非常不可能的,值得方便。 但這對你來說可能無法忍受。


作為最后的附注,如果 JSON 有一個原生的Date類型,那么可以避免的頭痛無疑是無數的......

暫無
暫無

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

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