我一直遇到與 TS 相同的“問題”,其中我有物件串列,并且每個物件都根據其型別具有不同的屬性。例如:
const widgets = [
{type: 'chart', chartType: 'line'},
{type: 'table', columnWidth: 100}
];
我可以為上面創建介面和型別并且它運行良好,但最后我總是遇到問題,即 TS 不夠“聰明”以識別我正在處理的物件型別。請參閱此游樂場示例
所以最后我總是不得不做類似的事情
(widget as WidgetChart).chartType
查看一些 SO 問題,我在@matthew-mullin 的這個答案中發現了一些非常相似的內容
現在您可以使用 Sublist 型別,它會根據您提供的欄位值正確推斷它是 SublistPartners 型別還是 SublistItem 型別。
但顯然在我的情況下不會發生這種情況。
是我做錯了什么還是我對 TS 的期望過高?
uj5u.com熱心網友回復:
我認為 TS 實際上是根據您鏈接的操場中的代碼正確推斷您的型別。您的問題是您將 TypeScript 與這一行“混淆”(*混淆基于我對您的真實意圖的理解):
type Widget = WidgetChart | WidgetBase;
具體來說,根據WidgetBase的定義(在您鏈接的操場上),
enum WidgetType {
CHART = 'chart',
TABLE = 'table'
}
interface WidgetBase {
type: WidgetType
title: string
}
{ type: 'chart', title: 'some string' }將是一個有效的 WidgetBase 并且因為Widget = WidgetChart | WidgetBase這個物件也將是一個有效的“小部件”!此物件與 的 switch case 匹配,type === 'chart'并且不包含對 的參考chartType。因此,TypeScript “正確地”警告您,chartType可能并不總是存在于具有“圖表”型別的 Widget 上(因為您定義的方式Widget)。
因此,糾正它,你將需要提供TS與實際WidgetTypes只,即:
enum WidgetType {
CHART = 'chart',
TABLE = 'table'
}
enum ChartType {
LINE = 'line',
BAR = 'bar'
}
interface WidgetBase {
title: string
// ... other common, *generic* properties ...
}
interface WidgetTable extends WidgetBase {
type: WidgetType.TABLE
columnWidth: number
// ... other specific properties ...
}
interface WidgetChart extends WidgetBase {
type: WidgetType.CHART,
chartType: ChartType
// ... other specific properties ...
}
type Widget = WidgetChart | WidgetTable; // <-- There are only two choices now: either a valid WidgetChart or a valid WidgetTable, nothing else! You can add more WidgetTypes here in a similar manner of course if you want
function renderWidget(widget: Widget){
switch(widget.type) {
case WidgetType.CHART:
return console.log(widget.chartType); // this works now and doesn't complain :)
case WidgetType.TABLE:
return console.log(widget.columnWidth); // so does this! :)
// you don't _need_ a default here in this case, since you've covered all the WidgetTypes
}
}
編輯:另一種選擇:
假設您有許多具有相同屬性(型別除外)的不同小部件型別,并且只有少數需要附加屬性,您可以采用稍微不同的方法。僅作為示例,具有相同屬性但型別不同的小部件將以不同的樣式呈現,例如:type = 'card-big' 或 type = 'card-small' 等。
在這種情況下,您可能不希望WidgetBase使用上述方法必須為 指定每個空擴展名。相反,您可以使用Exclude和Omit TS Utility Types。作業代碼將如下所示:
enum WidgetType {
CHART = 'chart',
TABLE = 'table',
CARD_BIG = 'card-big',
CARD_SMALL = 'card-small',
// ... etc ...
}
interface WidgetBase {
type: Exclude<WidgetType, WidgetType.CHART | WidgetType.TABLE>, // <-- this is the important bit - A generic widget is any widget *other than* chart or table
title: string
// ... other common, *generic* properties ...
}
interface WidgetTable extends Omit<WidgetBase, 'type'> { // <-- extend the base properties but omit the type which would clash otherwise
type: WidgetType.TABLE
columnWidth: number
// ... other specific properties ...
}
interface WidgetChart extends Omit<WidgetBase, 'type'> {
type: WidgetType.CHART,
chartType: ChartType
// ... other specific properties ...
}
type Widget = WidgetChart | WidgetTable | WidgetBase; // <-- specify the special cases the generic case without confusing TS because WidgetBase no longer overlaps with WidgetChart etc.
function renderWidget(widget: Widget) {
switch(widget.type) {
case WidgetType.CHART:
return console.log(widget.title, widget.chartType); // your specific widgets have both generic and specific properties
case WidgetType.TABLE:
return console.log(widget.title, widget.columnWidth); // same as above
// ... handle other generic widgets if you want, or ...
default:
return console.log(widget.title) // all your other widgets would have only the generic properties
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/375615.html
標籤:打字稿
