我想宣告物件 ( root) 的型別,它包含嵌套的物件陣列 ( values),其中每個物件都有屬性one(任何型別)和all(型別的陣列one)。
Bellow 是我嘗試這樣做的,但是我不知道如何獲得one財產型別,所以只有Value<any>:
type Value<T> = { one: T, all: T[] }
type Root = { values: Value<any>[] }
const root: Root = {
values: [
{ one: 1, all: 5 }, // Should be error, 'all' should be an array of numbers.
{ one: true, all: [5] }, // Should be error, 'all' should be array of booleans.
{ one: 'a', all: ['a', 'b', 'c'] }, // Ok.
{ one: true, all: [false, true, true] } // Ok.
]
}
有沒有辦法做到這一點?這是示例。如果可以不命名所有可能的型別組合,那就太棒了,例如:
type Value = { one: string, all: string[] } | { one: number, all: number[] } | { one: boolean, all: boolean[] }
因為它應該適用于任何型別。
uj5u.com熱心網友回復:
正如您所注意到的,您的Root值太寬而無法強制執行您關心的約束,但它確實具有使用簡單的優點:
const root: Root = {
values: [
{ one: true, all: [5] }, // uh oh no error
{ one: 'a', all: ['a', 'b', 'c'] }, // okay
{ one: true, all: [false, true, true] } // okay
]
}
const indexes = root.values.map(v => v.all.indexOf(v.one));
console.log(indexes) // [-1, 0, 1]
為了比較目的,我顯示了上述內容。以下方法將強制執行約束,但您最終會為此付出代價,但會失去一些便利。
當你發現自己希望你能寫一個無限聯合型別,比如 invalid
type SomeValue = Value<string> | Value<number> | Value<boolean>
| Value<null> | Value<Date> | Value<{a: string, b: number}> | ...
這表明您正在尋找存在量化的泛型型別。大多數具有泛型的語言,包括 TypeScript,只直接支持通用量化的泛型型別(對應于無限交叉型別)。microsoft/TypeScript#14466有一個功能請求來支持存在量化的泛型,但它還不是語言的一部分。
盡管如此,還是可以模擬這些泛型。存在泛型和通用泛型之間的區別與誰可以指定型別引數有關。如果你轉換資料提供者和資料消費者的角色,那么共性就會變成存在性。所以我們可以這樣編碼SomeValue:
type SomeValue = <R>(cb: <T>(value: Value<T>) => R) => R;
假設您有一個someValuetype值SomeValue。如果您想訪問底層Value<T>資料,您需要someValue()使用一些回呼來呼叫cb它,該回呼接收Value<T>并用它做一些事情。這就像一個立即解決的Promise. 該cb回呼必須為任何可能的制備Value<T>值; 誰提供value就可以選擇什么T。你只能說它是一些 T。
您可以撰寫一個輔助函式將 anyValue<T>變成 a SomeValue:
const toSomeValue = <T,>(value: Value<T>): SomeValue => cb => cb(value);
然后你Root會
type Root = { values: SomeValue[] }
這意味著您現在可以創建root如下:
const root: Root = {
values: [
toSomeValue({ one: true, all: [5] }), // error!
toSomeValue({ one: 'a', all: ['a', 'b', 'c'] }), // okay
toSomeValue({ one: true, all: [false, true, true] }) // okay
]
}
Here you've got the type checking you wanted (with the penalty that you needed to write toSomeValue() a bunch of times). And now you can make indexes from before by pushing your old v => v.all.indexOf(v.one) callback down into someV:
const indexes = root.values.map(someV => someV(v => v.all.indexOf(v.one)));
console.log(indexes) // [-1, 0, 1]
So it produces the same result, but again, with more complexity.
現在您可能想要嘗試使Root泛型本身,您將陣列型別映射到Value<T>單個T型別的陣列。然后你可以手動注釋root,比如說,Root<[5, "a" | "b" | "c", boolean]>或者嘗試讓編譯器從初始化器推斷 [5, "a" | "b" | "c", boolean]到values屬性。這些方法在技術上是可行的,但它們比上面的存在型別編碼增加了更多的復雜性,并且它們不是型別安全的。所以這里就不詳述了;盡管代碼包含在答案底部的鏈接中。
Playground 鏈接到代碼
uj5u.com熱心網友回復:
在您的情況下T是 any ,因為它可以是字串、數字或布林值。所以我不會推薦它。
你可以使用類似的東西:
type Value = number | string | boolean
type ValueObject = { one: Value; all: Value[] }
type Root = { values: ValueObject[] }
uj5u.com熱心網友回復:
最簡潔的答案是不; 我不相信有辦法做你想做的事。更長的答案是你可能不應該這樣做。
您可能可以像這樣存盤型別資訊:
const root: Root = {
values: [
{ one: 1, all: 5 } as Value<number>, // Should be error, 'all' should be an array of numbers.
{ one: true, all: [5] } as Value<boolean>, // Should be error, 'all' should be array of booleans.
{ one: 'a', all: ['a', 'b', 'c'] } as Value<string>, // Ok.
{ one: true, all: [false, true, true] } as Value<boolean> // Ok.
]
}
...但是,我正在努力考慮下游消費,您將在其中回圈root.values并且不需要條件來處理這樣的結構:
const process = (root) => {
switch(typeof root) {
case "number":
...
break
case "string":
...
break
case "boolean":
...
break
}
}
相反,您可以通過將 替換為以下內容來簡化整個流程root:
const intRoots: Value<number>[]
const boolRoots: Value<boolean>[]
const strRoots: Value<string>[]
然后在不進行型別檢查的情況下顯式處理每種型別:
const ***Roots.map(...)
我經常回退的合理化(盡管肯定有例外)是,如果我在處理程序中努力管理型別,那么值得重新審視資料結構
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/311191.html
上一篇:打字稿泛型:來自介面子項的只讀值
