簡體   English   中英

Typescript:擴展 JSON 類型的接口

[英]Typescript: interface that extends a JSON type

我在 typescript 中使用通用 JSON 類型,建議從這里

type JSONValue = 
 | string
 | number
 | boolean
 | null
 | JSONValue[]
 | {[key: string]: JSONValue}

我希望能夠從與 JSON 匹配的接口類型轉換為 JSON 類型。 例如:

interface Foo {
  name: 'FOO',
  fooProp: string
}

interface Bar {
  name: 'BAR',
  barProp: number;
}

const genericCall = (data: {[key: string]: JSONValue}): Foo | Bar | null => {
  if ('name' in data && data['name'] === 'FOO')
    return data as Foo;
  else if ('name' in data && data['name'] === 'BAR')
    return data as Bar;
  return null;
}

這目前失敗了,因為 Typescript 看不到接口如何與 JSONValue 具有相同類型:

Conversion of type '{ [key: string]: JSONValue; }' to type 'Foo' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Property 'name' is missing in type '{ [key: string]: JSONValue; }' but required in type 'Foo'.

但是從分析上我們當然知道這是可以的,因為我們認識到在運行時類型 Foo 和 BarJSON 兼容的。 我如何告訴 typescript 這是一個好的演員陣容?

ETA:我可以按照錯誤消息先轉換為未知,但我寧願不這樣做——如果 TS 真正理解差異會更好,我想知道這是否可能。

這里的問題是編譯器不使用檢查if ('name' in data && data['name'] === 'FOO')縮小data類型從其原始類型{[key: string]: JSONValue} {[key: string]: JSONValue}類型不是union ,當前in運算符僅檢查union 類型的窄值。 microsoft/TypeScript#21732有一個開放的功能請求來做這樣的縮小,但現在它不是語言的一部分。

這意味着data在檢查后保持{[key: string]: JSONValue}類型。 然后,當您嘗試通過data as Foo 斷言data的類型為Foo時,編譯器會警告您您可能犯了一個錯誤,因為它看不到Foo{[key: string]: JSONValue}是足夠相關。

如果你確定你所做的是一個很好的檢查,你總是可以使用編譯器建議和類型斷言到與Foo{[key: string]: JSONValue}相關的中間類型,例如unknown

return data as unknown as Foo; // okay

如果這與您有關,那么您可以編寫自己的用戶定義類型保護 function執行您期望的縮小范圍if ('name' in data && data['name'] === 'FOO') 本質上,如果該檢查通過,那么我們知道data的類型是{name: 'FOO'} ,它與Foo足夠相關以進行類型斷言。 這是一個可能的類型保護 function:

function hasKeyVal<K extends PropertyKey, V extends string | number |
  boolean | null | undefined | bigint>(
    obj: any, k: K, v: V): obj is { [P in K]: V } {
  return obj && obj[k] === v;
}

因此,而不是if ('name' in data && data['name'] === 'FOO') ,你寫if (hasKeyVal(data, 'name', 'FOO')) 返回類型obj is {[P in K]: V}意味着如果 function 返回true ,編譯器應將obj的類型縮小為具有鍵為K類型且值為V類型的屬性的類型。 讓我們測試一下:

const genericCall = (data: { [key: string]: JSONValue }): Foo | Bar | null => {
  if (hasKeyVal(data, 'name', 'FOO'))
    return data as Foo; // okay, data is now {name: 'FOO'} which is related to Foo
  else if (hasKeyVal(data, 'name', 'BAR'))
    return data as Bar;  // okay, data is now {name: 'BAR'} which is related to Bar
  return null;
}

現在它起作用了。 hasKeyVal()檢查將data縮小到具有正確類型的name屬性的內容,這與FooBar足夠相關,以便類型斷言成功(類型斷言仍然是必要的,因為類型{name: 'Foo'}可能不是Foo如果Foo有其他屬性)。

Playground 代碼鏈接

暫無
暫無

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

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